From 6ab80b62c543212916c1b9e1d300a160b78e0ae5 Mon Sep 17 00:00:00 2001 From: Alexey Akhunov Date: Wed, 18 Jul 2018 13:16:41 +0100 Subject: [PATCH] Handle interrupts, resume loading --- main.go | 105 +++++++++++++++++++++++++++------------------- state/database.go | 4 ++ 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/main.go b/main.go index 5d88e155..3ed6f1f4 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,15 @@ package main import ( "bytes" + "encoding/binary" "encoding/json" "flag" "fmt" "io" "os" + "os/signal" "runtime/pprof" + "syscall" "time" "github.com/cosmos/ethermint/core" @@ -30,7 +33,6 @@ import ( var cpuprofile = flag.String("cpu-profile", "", "write cpu profile `file`") var blockchain = flag.String("blockchain", "data/blockchain", "file containing blocks to load") var datadir = flag.String("datadir", "", "directory for ethermint data") -var blockStop = flag.Int("stop-block", 10000, "stop prematurely after processing a certain number of blocks") var ( // TODO: Document... @@ -57,6 +59,14 @@ func main() { defer pprof.StopCPUProfile() } + sigs := make(chan os.Signal, 1) + interruptCh := make(chan bool, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigs + interruptCh <- true + }() + stateDB := dbm.NewDB("state", dbm.LevelDBBackend, *datadir) codeDB := dbm.NewDB("code", dbm.LevelDBBackend, *datadir) @@ -65,38 +75,41 @@ func main() { panic(err) } - // start with empty root hash (i.e. empty state) - gethStateDB, err := ethstate.New(ethcommon.Hash{}, ethermintDB) - if err != nil { - panic(err) - } - - genBlock := ethcore.DefaultGenesisBlock() - for addr, account := range genBlock.Alloc { - gethStateDB.AddBalance(addr, account.Balance) - gethStateDB.SetCode(addr, account.Code) - gethStateDB.SetNonce(addr, account.Nonce) - - for key, value := range account.Storage { - gethStateDB.SetState(addr, key, value) + // Only create genesis if it is a brand new database + if ethermintDB.LatestVersion() == 0 { + // start with empty root hash (i.e. empty state) + gethStateDB, err := ethstate.New(ethcommon.Hash{}, ethermintDB) + if err != nil { + panic(err) } + + genBlock := ethcore.DefaultGenesisBlock() + for addr, account := range genBlock.Alloc { + gethStateDB.AddBalance(addr, account.Balance) + gethStateDB.SetCode(addr, account.Code) + gethStateDB.SetNonce(addr, account.Nonce) + + for key, value := range account.Storage { + gethStateDB.SetState(addr, key, value) + } + } + + // get balance of one of the genesis account having 200 ETH + b := gethStateDB.GetBalance(genInvestor) + fmt.Printf("balance of %s: %s\n", genInvestor.String(), b) + + // commit the geth stateDB with 'false' to delete empty objects + genRoot, err := gethStateDB.Commit(false) + if err != nil { + panic(err) + } + + commitID := ethermintDB.Commit() + + fmt.Printf("commitID after genesis: %v\n", commitID) + fmt.Printf("genesis state root hash: %x\n", genRoot[:]) } - // get balance of one of the genesis account having 200 ETH - b := gethStateDB.GetBalance(genInvestor) - fmt.Printf("balance of %s: %s\n", genInvestor.String(), b) - - // commit the geth stateDB with 'false' to delete empty objects - genRoot, err := gethStateDB.Commit(false) - if err != nil { - panic(err) - } - - commitID := ethermintDB.Commit() - - fmt.Printf("commitID after genesis: %v\n", commitID) - fmt.Printf("genesis state root hash: %x\n", genRoot[:]) - // file with blockchain data exported from geth by using "geth exportdb" // command. // @@ -120,14 +133,18 @@ func main() { root501 ethcommon.Hash // root hash after block 501 ) - prevRoot := genRoot + var prevRoot ethcommon.Hash + binary.BigEndian.PutUint64(prevRoot[:8], uint64(ethermintDB.LatestVersion())) + ethermintDB.Tracing = true chainContext := core.NewChainContext() vmConfig := ethvm.Config{} n := 0 startTime := time.Now() - for { + interrupt := false + var lastSkipped uint64 + for !interrupt { if err = stream.Decode(&block); err == io.EOF { err = nil break @@ -135,10 +152,15 @@ func main() { panic(fmt.Errorf("failed to decode at block %d: %s", block.NumberU64(), err)) } - // don't import first block - if block.NumberU64() == 0 { + // don't import blocks already imported + if block.NumberU64() < uint64(ethermintDB.LatestVersion()) { + lastSkipped = block.NumberU64() continue } + if lastSkipped > 0 { + fmt.Printf("Skipped blocks up to %d\n", lastSkipped) + lastSkipped = 0 + } header := block.Header() chainContext.Coinbase = header.Coinbase @@ -225,9 +247,11 @@ func main() { fmt.Printf("processed %d blocks, time so far: %v\n", n, time.Since(startTime)) } - if *blockStop == n { - fmt.Println("haulting process prematurely") - break + // Check for interrupts + select { + case interrupt = <-interruptCh: + fmt.Printf("Interrupted, please wait for cleanup...\n") + default: } } @@ -235,13 +259,6 @@ func main() { ethermintDB.Tracing = true - genState, err := ethstate.New(genRoot, ethermintDB) - if err != nil { - panic(err) - } - - fmt.Printf("balance of one of the genesis investors: %s\n", genState.GetBalance(genInvestor)) - // try to create a new geth stateDB from root of the block 500 fmt.Printf("root500: %x\n", root500[:]) diff --git a/state/database.go b/state/database.go index 6d00289c..e5dcbb24 100644 --- a/state/database.go +++ b/state/database.go @@ -95,6 +95,10 @@ func NewDatabase(stateDB, codeDB dbm.DB) (*Database, error) { return db, nil } +func (db *Database) LatestVersion() int64 { + return db.stateStore.LastCommitID().Version +} + // OpenTrie implements Ethereum's state.Database interface. It returns a Trie // type which implements the Ethereum state.Trie interface. It us used for // storage of accounts. An error is returned if state cannot load for a