Skip to content

Commit

Permalink
core/state: access trie through Database interface, track errors (#14589
Browse files Browse the repository at this point in the history
)

With this commit, core/state's access to the underlying key/value database is
mediated through an interface. Database errors are tracked in StateDB and
returned by CommitTo or the new Error method.

Motivation for this change: We can remove the light client's duplicated copy of
core/state. The light client now supports node iteration, so tracing and storage
enumeration can work with the light client (not implemented in this commit).
  • Loading branch information
fjl authored Jun 27, 2017
1 parent bb36627 commit 9e5f03b
Show file tree
Hide file tree
Showing 49 changed files with 809 additions and 1,663 deletions.
4 changes: 2 additions & 2 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (b *SimulatedBackend) Rollback() {
func (b *SimulatedBackend) rollback() {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
}

// CodeAt returns the code associated with a certain account in the blockchain.
Expand Down Expand Up @@ -279,7 +279,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
block.AddTx(tx)
})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
return nil
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/evm/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ func runCmd(ctx *cli.Context) error {
_, statedb = gen.ToBlock()
chainConfig = gen.Config
} else {
var db, _ = ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, db)
db, _ := ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
}
if ctx.GlobalString(SenderFlag.Name) != "" {
sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
Expand Down Expand Up @@ -188,7 +188,7 @@ func runCmd(ctx *cli.Context) error {
execTime := time.Since(tstart)

if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true)
statedb.IntermediateRoot(true)
fmt.Println(string(statedb.Dump()))
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func dump(ctx *cli.Context) error {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
state, err := state.New(block.Root(), chainDb)
state, err := state.New(block.Root(), state.NewDatabase(chainDb))
if err != nil {
utils.Fatalf("could not create new state: %v", err)
}
Expand Down
12 changes: 3 additions & 9 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,10 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
// validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block's known, and if not, that it's linkable
if v.bc.HasBlock(block.Hash()) {
if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
return ErrKnownBlock
}
if v.bc.HasBlockAndState(block.Hash()) {
return ErrKnownBlock
}
parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
if !v.bc.HasBlockAndState(block.ParentHash()) {
return consensus.ErrUnknownAncestor
}
// Header validity is known at this point, check the uncles and transactions
Expand Down
38 changes: 16 additions & 22 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type BlockChain struct {
currentBlock *types.Block // Current head of the block chain
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)

stateCache *state.StateDB // State database to reuse between imports (contains state cache)
stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks
Expand Down Expand Up @@ -125,6 +125,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
bc := &BlockChain{
config: config,
chainDb: chainDb,
stateCache: state.NewDatabase(chainDb),
eventMux: mux,
quit: make(chan struct{}),
bodyCache: bodyCache,
Expand Down Expand Up @@ -190,7 +191,7 @@ func (bc *BlockChain) loadLastState() error {
return bc.Reset()
}
// Make sure the state associated with the block is available
if _, err := state.New(currentBlock.Root(), bc.chainDb); err != nil {
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Dangling block without a state associated, init from scratch
log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
return bc.Reset()
Expand All @@ -214,12 +215,6 @@ func (bc *BlockChain) loadLastState() error {
bc.currentFastBlock = block
}
}
// Initialize a statedb cache to ensure singleton account bloom filter generation
statedb, err := state.New(bc.currentBlock.Root(), bc.chainDb)
if err != nil {
return err
}
bc.stateCache = statedb

// Issue a status log for the user
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
Expand Down Expand Up @@ -261,7 +256,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
}
if bc.currentBlock != nil {
if _, err := state.New(bc.currentBlock.Root(), bc.chainDb); err != nil {
if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
bc.currentBlock = nil
}
Expand Down Expand Up @@ -384,7 +379,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {

// StateAt returns a new mutable state based on a particular point in time.
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return bc.stateCache.New(root)
return state.New(root, bc.stateCache)
}

// Reset purges the entire blockchain, restoring it to its genesis state.
Expand Down Expand Up @@ -531,7 +526,7 @@ func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool {
return false
}
// Ensure the associated state is also present
_, err := state.New(block.Root(), bc.chainDb)
_, err := bc.stateCache.OpenTrie(block.Root())
return err == nil
}

Expand Down Expand Up @@ -959,31 +954,30 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
}
// Create a new statedb using the parent block and report an
// error if it fails.
switch {
case i == 0:
err = bc.stateCache.Reset(bc.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
default:
err = bc.stateCache.Reset(chain[i-1].Root())
var parent *types.Block
if i == 0 {
parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
} else {
parent = chain[i-1]
}
state, err := state.New(parent.Root(), bc.stateCache)
if err != nil {
bc.reportBlock(block, nil, err)
return i, err
}
// Process block using the parent state as reference point.
receipts, logs, usedGas, err := bc.processor.Process(block, bc.stateCache, bc.vmConfig)
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
if err != nil {
bc.reportBlock(block, receipts, err)
return i, err
}
// Validate the state using the default validator
err = bc.Validator().ValidateState(block, bc.GetBlock(block.ParentHash(), block.NumberU64()-1), bc.stateCache, receipts, usedGas)
err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)
if err != nil {
bc.reportBlock(block, receipts, err)
return i, err
}
// Write state changes to database
_, err = bc.stateCache.Commit(bc.config.IsEIP158(block.Number()))
if err != nil {
if _, err = state.CommitTo(bc.chainDb, bc.config.IsEIP158(block.Number())); err != nil {
return i, err
}

Expand Down Expand Up @@ -1021,7 +1015,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
// Write hash preimages
if err := WritePreimages(bc.chainDb, block.NumberU64(), bc.stateCache.Preimages()); err != nil {
if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil {
return i, err
}
case SideStatTy:
Expand Down
10 changes: 5 additions & 5 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
}
return err
}
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.chainDb)
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache)
if err != nil {
return err
}
Expand All @@ -148,7 +148,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.mu.Lock()
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
WriteBlock(blockchain.chainDb, block)
statedb.Commit(false)
statedb.CommitTo(blockchain.chainDb, false)
blockchain.mu.Unlock()
}
return nil
Expand Down Expand Up @@ -1131,23 +1131,23 @@ func TestEIP161AccountRemoval(t *testing.T) {
if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
t.Fatal(err)
}
if !blockchain.stateCache.Exist(theAddr) {
if st, _ := blockchain.State(); !st.Exist(theAddr) {
t.Error("expected account to exist")
}

// account needs to be deleted post eip 161
if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
t.Fatal(err)
}
if blockchain.stateCache.Exist(theAddr) {
if st, _ := blockchain.State(); st.Exist(theAddr) {
t.Error("account should not exist")
}

// account musn't be created post eip 161
if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
t.Fatal(err)
}
if blockchain.stateCache.Exist(theAddr) {
if st, _ := blockchain.State(); st.Exist(theAddr) {
t.Error("account should not exist")
}
}
4 changes: 2 additions & 2 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,15 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Dat
gen(i, b)
}
ethash.AccumulateRewards(statedb, h, b.uncles)
root, err := statedb.Commit(config.IsEIP158(h.Number))
root, err := statedb.CommitTo(db, config.IsEIP158(h.Number))
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
h.Root = root
return types.NewBlock(h, b.txs, b.uncles, b.receipts), b.receipts
}
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), db)
statedb, err := state.New(parent.Root(), state.NewDatabase(db))
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
// ToBlock creates the block and state of a genesis specification.
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
statedb.SetCode(addr, account.Code)
Expand Down
Loading

0 comments on commit 9e5f03b

Please sign in to comment.