Skip to content

Commit 92ee78f

Browse files
authored
Merge pull request #389 from uprendis/feature/incremental-genesis
Incremental genesis
2 parents ada20db + a5f10b5 commit 92ee78f

File tree

7 files changed

+176
-48
lines changed

7 files changed

+176
-48
lines changed

cmd/opera/launcher/chaincmd.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@ import (
88
var (
99
EvmExportMode = cli.StringFlag{
1010
Name: "export.evm.mode",
11-
Usage: `EVM export mode ("full" or "ext-mpt" or "mpt" or "none")`,
11+
Usage: `EVM export mode ("full" or "ext-mpt" or "mpt")`,
1212
Value: "mpt",
1313
}
14+
EvmExportExclude = cli.StringFlag{
15+
Name: "export.evm.exclude",
16+
Usage: `DB of EVM keys to exclude from genesis`,
17+
}
18+
GenesisExportSections = cli.StringFlag{
19+
Name: "export.sections",
20+
Usage: `Genesis sections to export separated by comma (e.g. "brs-1" or "ers" or "evm-2")`,
21+
Value: "brs,ers,evm",
22+
}
1423
importCommand = cli.Command{
1524
Name: "import",
1625
Usage: "Import a blockchain file",
@@ -76,11 +85,13 @@ be gzipped
7685
{
7786
Name: "genesis",
7887
Usage: "Export current state into a genesis file",
79-
ArgsUsage: "<filename or dry-run> [<epochFrom> <epochTo>] [--export.evm.mode=none]",
88+
ArgsUsage: "<filename or dry-run> [<epochFrom> <epochTo>] [--export.evm.mode=MODE --export.evm.exclude=DB_PATH --export.sections=A,B,C]",
8089
Action: utils.MigrateFlags(exportGenesis),
8190
Flags: []cli.Flag{
8291
DataDirFlag,
8392
EvmExportMode,
93+
EvmExportExclude,
94+
GenesisExportSections,
8495
},
8596
Description: `
8697
opera export genesis
@@ -91,6 +102,20 @@ Optional second and third arguments control the first and
91102
last epoch to write.
92103
Pass dry-run instead of filename for calculation of hashes without exporting data.
93104
EVM export mode is configured with --export.evm.mode.
105+
`,
106+
},
107+
{
108+
Name: "evm-keys",
109+
Usage: "Export EVM node keys",
110+
ArgsUsage: "<directory>",
111+
Action: utils.MigrateFlags(exportEvmKeys),
112+
Flags: []cli.Flag{
113+
DataDirFlag,
114+
},
115+
Description: `
116+
opera export evm-keys
117+
118+
Requires a first argument of the DB directory to write to.
94119
`,
95120
},
96121
},

cmd/opera/launcher/check.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ func checkEvm(ctx *cli.Context) error {
2727
start, reported := time.Now(), time.Now()
2828

2929
var prevPoint idx.Block
30+
var prevIndex idx.Block
3031
checkBlocks := func(stateOK func(root common.Hash) (bool, error)) {
3132
var (
3233
lastIdx = gdb.GetLatestBlockIndex()
3334
prevPointRootExist bool
3435
)
3536
gdb.ForEachBlock(func(index idx.Block, block *inter.Block) {
37+
prevIndex = index
3638
found, err := stateOK(common.Hash(block.Root))
3739
if found != prevPointRootExist {
3840
if index > 0 && found {
@@ -57,9 +59,9 @@ func checkEvm(ctx *cli.Context) error {
5759
})
5860
}
5961

60-
if err := evms.CheckEvm(checkBlocks); err != nil {
62+
if err := evms.CheckEvm(checkBlocks, true); err != nil {
6163
return err
6264
}
63-
log.Info("EVM storage is verified", "last", prevPoint, "elapsed", common.PrettyDuration(time.Since(start)))
65+
log.Info("EVM storage is verified", "last", prevIndex, "elapsed", common.PrettyDuration(time.Since(start)))
6466
return nil
6567
}

cmd/opera/launcher/export.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import (
1010

1111
"github.com/Fantom-foundation/lachesis-base/hash"
1212
"github.com/Fantom-foundation/lachesis-base/inter/idx"
13+
"github.com/Fantom-foundation/lachesis-base/kvdb/batched"
14+
"github.com/Fantom-foundation/lachesis-base/kvdb/pebble"
1315
"github.com/ethereum/go-ethereum/cmd/utils"
1416
"github.com/ethereum/go-ethereum/common"
1517
"github.com/ethereum/go-ethereum/log"
1618
"github.com/ethereum/go-ethereum/rlp"
1719
"github.com/status-im/keycard-go/hexutils"
20+
"github.com/syndtr/goleveldb/leveldb/opt"
1821
"gopkg.in/urfave/cli.v1"
1922

2023
"github.com/Fantom-foundation/go-opera/gossip"
@@ -114,3 +117,38 @@ func exportTo(w io.Writer, gdb *gossip.Store, from, to idx.Epoch) (err error) {
114117

115118
return
116119
}
120+
121+
func exportEvmKeys(ctx *cli.Context) error {
122+
if len(ctx.Args()) < 1 {
123+
utils.Fatalf("This command requires an argument.")
124+
}
125+
126+
cfg := makeAllConfigs(ctx)
127+
128+
rawDbs := makeDirectDBsProducer(cfg)
129+
gdb := makeGossipStore(rawDbs, cfg)
130+
defer gdb.Close()
131+
132+
fn := ctx.Args().First()
133+
134+
keysDB_, err := pebble.New(fn, 1024*opt.MiB, utils.MakeDatabaseHandles()/2, nil, nil)
135+
if err != nil {
136+
return err
137+
}
138+
keysDB := batched.Wrap(keysDB_)
139+
defer keysDB.Close()
140+
141+
it := gdb.EvmStore().EvmDb.NewIterator(nil, nil)
142+
// iterate only over MPT data
143+
it = mptAndPreimageIterator{it}
144+
defer it.Release()
145+
146+
log.Info("Exporting EVM keys", "dir", fn)
147+
for it.Next() {
148+
if err := keysDB.Put(it.Key(), []byte{0}); err != nil {
149+
return err
150+
}
151+
}
152+
log.Info("Exported EVM keys", "dir", fn)
153+
return nil
154+
}

cmd/opera/launcher/genesiscmd.go

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ import (
99
"os"
1010
"path"
1111
"strconv"
12+
"strings"
1213

1314
"github.com/Fantom-foundation/lachesis-base/common/bigendian"
1415
"github.com/Fantom-foundation/lachesis-base/hash"
1516
"github.com/Fantom-foundation/lachesis-base/inter/idx"
1617
"github.com/Fantom-foundation/lachesis-base/kvdb"
18+
"github.com/Fantom-foundation/lachesis-base/kvdb/pebble"
1719
"github.com/ethereum/go-ethereum/cmd/utils"
1820
"github.com/ethereum/go-ethereum/log"
1921
"github.com/ethereum/go-ethereum/rlp"
22+
"github.com/syndtr/goleveldb/leveldb/opt"
2023
"gopkg.in/urfave/cli.v1"
2124

25+
"github.com/Fantom-foundation/go-opera/gossip"
2226
"github.com/Fantom-foundation/go-opera/gossip/evmstore"
2327
"github.com/Fantom-foundation/go-opera/inter/ibr"
2428
"github.com/Fantom-foundation/go-opera/inter/ier"
@@ -65,6 +69,20 @@ func (it mptAndPreimageIterator) Next() bool {
6569
return false
6670
}
6771

72+
type excludingIterator struct {
73+
kvdb.Iterator
74+
exclude kvdb.Reader
75+
}
76+
77+
func (it excludingIterator) Next() bool {
78+
for it.Iterator.Next() {
79+
if ok, _ := it.exclude.Has(it.Key()); !ok {
80+
return true
81+
}
82+
}
83+
return false
84+
}
85+
6886
type unitWriter struct {
6987
plain io.WriteSeeker
7088
gziper *gzip.Writer
@@ -176,6 +194,14 @@ func (w *unitWriter) Write(b []byte) (n int, err error) {
176194
return
177195
}
178196

197+
func getEpochBlock(epoch idx.Epoch, store *gossip.Store) idx.Block {
198+
bs, _ := store.GetHistoryBlockEpochState(epoch)
199+
if bs == nil {
200+
return 0
201+
}
202+
return bs.LastBlock.Idx
203+
}
204+
179205
func exportGenesis(ctx *cli.Context) error {
180206
if len(ctx.Args()) < 1 {
181207
utils.Fatalf("This command requires an argument.")
@@ -198,8 +224,35 @@ func exportGenesis(ctx *cli.Context) error {
198224
to = idx.Epoch(n)
199225
}
200226
mode := ctx.String(EvmExportMode.Name)
201-
if mode != "full" && mode != "ext-mpt" && mode != "mpt" && mode != "none" {
202-
return errors.New("--export.evm.mode must be one of {full, ext-mpt, mpt, none}")
227+
if mode != "full" && mode != "ext-mpt" && mode != "mpt" {
228+
return errors.New("--export.evm.mode must be one of {full, ext-mpt, mpt}")
229+
}
230+
231+
var excludeEvmDB kvdb.Store
232+
if excludeEvmDBPath := ctx.String(EvmExportExclude.Name); len(excludeEvmDBPath) > 0 {
233+
db, err := pebble.New(excludeEvmDBPath, 1024*opt.MiB, utils.MakeDatabaseHandles()/2, nil, nil)
234+
if err != nil {
235+
return err
236+
}
237+
excludeEvmDB = db
238+
}
239+
240+
sectionsStr := ctx.String(GenesisExportSections.Name)
241+
sections := map[string]string{}
242+
for _, str := range strings.Split(sectionsStr, ",") {
243+
before := len(sections)
244+
if strings.HasPrefix(str, "brs") {
245+
sections["brs"] = str
246+
} else if strings.HasPrefix(str, "ers") {
247+
sections["ers"] = str
248+
} else if strings.HasPrefix(str, "evm") {
249+
sections["evm"] = str
250+
} else {
251+
return fmt.Errorf("unknown section '%s': has to start with either 'brs' or 'ers' or 'evm'", str)
252+
}
253+
if len(sections) == before {
254+
return fmt.Errorf("duplicate section: '%s'", str)
255+
}
203256
}
204257

205258
cfg := makeAllConfigs(ctx)
@@ -243,12 +296,10 @@ func exportGenesis(ctx *cli.Context) error {
243296
if to > gdb.GetEpoch() {
244297
to = gdb.GetEpoch()
245298
}
246-
toBlock := idx.Block(0)
247-
fromBlock := idx.Block(0)
248-
{
299+
if len(sections["ers"]) > 0 {
249300
log.Info("Exporting epochs", "from", from, "to", to)
250301
writer := newUnitWriter(plain)
251-
err := writer.Start(header, genesisstore.EpochsSection, tmpPath)
302+
err := writer.Start(header, sections["ers"], tmpPath)
252303
if err != nil {
253304
return err
254305
}
@@ -266,28 +317,29 @@ func exportGenesis(ctx *cli.Context) error {
266317
if err != nil {
267318
return err
268319
}
269-
if i == from {
270-
fromBlock = er.BlockState.LastBlock.Idx
271-
}
272-
if i == to {
273-
toBlock = er.BlockState.LastBlock.Idx
274-
}
275320
}
276321
epochsHash, err = writer.Flush()
277322
if err != nil {
278323
return err
279324
}
280-
log.Info("Exported epochs", "hash", epochsHash.String())
325+
log.Info("Exported epochs")
326+
fmt.Printf("- Epochs hash: %v \n", epochsHash.String())
281327
}
282328

283-
if fromBlock < 1 {
284-
// avoid underflow
285-
fromBlock = 1
286-
}
287-
{
329+
if len(sections["brs"]) > 0 {
330+
toBlock := getEpochBlock(to, gdb)
331+
fromBlock := getEpochBlock(from, gdb)
332+
if sections["brs"] != "brs" {
333+
// to continue prev section, include blocks of prev epochs too, excluding first blocks of prev epoch (which is last block if prev section)
334+
fromBlock = getEpochBlock(from-1, gdb) + 1
335+
}
336+
if fromBlock < 1 {
337+
// avoid underflow
338+
fromBlock = 1
339+
}
288340
log.Info("Exporting blocks", "from", fromBlock, "to", toBlock)
289341
writer := newUnitWriter(plain)
290-
err := writer.Start(header, genesisstore.BlocksSection, tmpPath)
342+
err := writer.Start(header, sections["brs"], tmpPath)
291343
if err != nil {
292344
return err
293345
}
@@ -313,13 +365,14 @@ func exportGenesis(ctx *cli.Context) error {
313365
if err != nil {
314366
return err
315367
}
316-
log.Info("Exported blocks", "hash", blocksHash.String())
368+
log.Info("Exported blocks")
369+
fmt.Printf("- Blocks hash: %v \n", blocksHash.String())
317370
}
318371

319-
if mode != "none" {
320-
log.Info("Exporting EVM data", "from", fromBlock, "to", toBlock)
372+
if len(sections["evm"]) > 0 {
373+
log.Info("Exporting EVM data")
321374
writer := newUnitWriter(plain)
322-
err := writer.Start(header, genesisstore.EvmSection, tmpPath)
375+
err := writer.Start(header, sections["evm"], tmpPath)
323376
if err != nil {
324377
return err
325378
}
@@ -331,6 +384,9 @@ func exportGenesis(ctx *cli.Context) error {
331384
// iterate only over MPT data and preimages
332385
it = mptAndPreimageIterator{it}
333386
}
387+
if excludeEvmDB != nil {
388+
it = excludingIterator{it, excludeEvmDB}
389+
}
334390
defer it.Release()
335391
err = iodb.Write(writer, it)
336392
if err != nil {
@@ -340,12 +396,9 @@ func exportGenesis(ctx *cli.Context) error {
340396
if err != nil {
341397
return err
342398
}
343-
log.Info("Exported EVM data", "hash", evmHash.String())
399+
log.Info("Exported EVM data")
400+
fmt.Printf("- EVM hash: %v \n", evmHash.String())
344401
}
345402

346-
fmt.Printf("- Epochs hash: %v \n", epochsHash.String())
347-
fmt.Printf("- Blocks hash: %v \n", blocksHash.String())
348-
fmt.Printf("- EVM hash: %v \n", evmHash.String())
349-
350403
return nil
351404
}

gossip/evmstore/utils.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var (
2828
emptyHash = common.Hash{}
2929
)
3030

31-
func (s *Store) CheckEvm(forEachState func(func(root common.Hash) (found bool, err error))) error {
31+
func (s *Store) CheckEvm(forEachState func(func(root common.Hash) (found bool, err error)), onlyRoots bool) error {
3232
log.Info("Checking every node hash")
3333
nodeIt := s.table.Evm.NewIterator(nil, nil)
3434
defer nodeIt.Release()
@@ -68,7 +68,11 @@ func (s *Store) CheckEvm(forEachState func(func(root common.Hash) (found bool, e
6868
}
6969
}
7070

71-
log.Info("Checking presence of every node")
71+
if onlyRoots {
72+
log.Info("Checking presence of every root")
73+
} else {
74+
log.Info("Checking presence of every node")
75+
}
7276
var (
7377
visitedHashes = make([]common.Hash, 0, 1000000)
7478
visitedI = 0
@@ -88,7 +92,7 @@ func (s *Store) CheckEvm(forEachState func(func(root common.Hash) (found bool, e
8892
forEachState(func(root common.Hash) (found bool, err error) {
8993
stateTrie, err := s.EvmState.OpenTrie(root)
9094
found = stateTrie != nil && err == nil
91-
if !found {
95+
if !found || onlyRoots {
9296
return
9397
}
9498

opera/genesisstore/disk.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"strings"
910
"time"
1011

1112
"github.com/Fantom-foundation/lachesis-base/common/bigendian"
@@ -133,14 +134,19 @@ func OpenGenesisStore(rawReader ReadAtSeekerCloser) (*Store, genesis.Hashes, err
133134
// wrap with a logger
134135
// human-readable name
135136
name := unit.UnitName
136-
if unit.UnitName == BlocksSection {
137-
name = "blocks"
137+
scanfName := strings.ReplaceAll(name, "-", "")
138+
if scanfName[len(scanfName)-1] < '0' || scanfName[len(scanfName)-1] > '9' {
139+
scanfName += "0"
138140
}
139-
if unit.UnitName == EpochsSection {
140-
name = "epochs"
141+
var part int
142+
if _, err := fmt.Sscanf(scanfName, "brs%d", &part); err == nil {
143+
name = fmt.Sprintf("blocks unit %d", part)
141144
}
142-
if unit.UnitName == EvmSection {
143-
name = "EVM data"
145+
if _, err := fmt.Sscanf(scanfName, "ers%d", &part); err == nil {
146+
name = fmt.Sprintf("epochs unit %d", part)
147+
}
148+
if _, err := fmt.Sscanf(scanfName, "evm%d", &part); err == nil {
149+
name = fmt.Sprintf("EVM unit %d", part)
144150
}
145151
loggedReader := filelog.Wrap(gzipReader, name, uncompressedSize, time.Minute)
146152

0 commit comments

Comments
 (0)