core, eth, main, state, tests: Return state changes from statedb.Commit
This commit is contained in:
parent
0f77f34bb6
commit
0505976975
@ -686,7 +686,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
|
||||
root = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
if idx == int(txIndex) {
|
||||
// This is to make sure root can be opened by OpenTrie
|
||||
root, err = statedb.Commit(api.chainConfig.IsEIP158(block.Number()))
|
||||
root, _, err = statedb.Commit(api.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return AccountRangeResult{}, err
|
||||
}
|
||||
@ -796,7 +796,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||
_ = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
if idx == int(txIndex) {
|
||||
// This is to make sure root can be opened by OpenTrie
|
||||
_, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
_, _, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return StorageRangeResult{}, err
|
||||
}
|
||||
|
||||
@ -1434,10 +1434,13 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
}
|
||||
// Commit all cached state changes into underlying memory database.
|
||||
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
root, stateChanges, commitErr := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
|
||||
log.Debug("StateChanges", "count", len(stateChanges))
|
||||
|
||||
if commitErr != nil {
|
||||
return NonStatTy, commitErr
|
||||
}
|
||||
|
||||
triedb := bc.stateCache.TrieDB()
|
||||
|
||||
// If we're running an archive node, always flush
|
||||
|
||||
@ -216,7 +216,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
||||
|
||||
// Write state changes to db
|
||||
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||
root, _, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ func TestSnapshot2(t *testing.T) {
|
||||
so0.deleted = false
|
||||
state.setStateObject(so0)
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
// and one with deleted == true
|
||||
|
||||
@ -802,28 +802,51 @@ func (s *StateDB) clearJournalAndRefund() {
|
||||
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entires
|
||||
}
|
||||
|
||||
// ModifiedAccount is an Account and it's Storage trie that has changed in the current block.
|
||||
type ModifiedAccount struct {
|
||||
Account
|
||||
Storage
|
||||
}
|
||||
|
||||
// StateChanges are a map between an Account's address to it's ModifiedAccount.
|
||||
type StateChanges map[common.Address]ModifiedAccount
|
||||
|
||||
// Commit writes the state to the underlying in-memory trie database.
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, StateChanges, error) {
|
||||
if s.dbErr != nil {
|
||||
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||
return common.Hash{}, nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||
}
|
||||
|
||||
// Finalize any pending changes and merge everything into the tries
|
||||
s.IntermediateRoot(deleteEmptyObjects)
|
||||
|
||||
stateChanges := make(StateChanges)
|
||||
|
||||
// Commit objects to the trie, measuring the elapsed time
|
||||
for addr := range s.stateObjectsDirty {
|
||||
modifiedAccount := ModifiedAccount{}
|
||||
|
||||
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||
// Write any contract code associated with the state object
|
||||
if obj.code != nil && obj.dirtyCode {
|
||||
s.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code)
|
||||
obj.dirtyCode = false
|
||||
}
|
||||
|
||||
// Add the account to the modifiedAccounts map
|
||||
modifiedAccount = ModifiedAccount{Account: obj.data}
|
||||
// Add the origin storage to the modifiedAccounts map before it's committed (and flushed from in-memory)
|
||||
modifiedAccount.Storage = obj.originStorage
|
||||
|
||||
// Write any storage changes in the state object to its storage trie
|
||||
if err := obj.CommitTrie(s.db); err != nil {
|
||||
return common.Hash{}, err
|
||||
return common.Hash{}, StateChanges{}, err
|
||||
}
|
||||
}
|
||||
|
||||
stateChanges[addr] = modifiedAccount
|
||||
}
|
||||
|
||||
if len(s.stateObjectsDirty) > 0 {
|
||||
s.stateObjectsDirty = make(map[common.Address]struct{})
|
||||
}
|
||||
@ -867,5 +890,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
}
|
||||
s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil
|
||||
}
|
||||
return root, err
|
||||
|
||||
return root, stateChanges, err
|
||||
}
|
||||
|
||||
@ -67,6 +67,114 @@ func TestUpdateLeaks(t *testing.T) {
|
||||
it.Release()
|
||||
}
|
||||
|
||||
func TestStateChangesEmittedFromCommit(t *testing.T) {
|
||||
// Create an empty state database
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
state, _ := New(common.Hash{}, NewDatabase(db), nil)
|
||||
|
||||
// Commit 1: Create accounts with storage
|
||||
expectedStateChangesOne := make(StateChanges)
|
||||
|
||||
for i := byte(0); i < 2; i++ {
|
||||
addr := common.BytesToAddress([]byte{i})
|
||||
modifiedAccount := ModifiedAccount{
|
||||
Storage: make(map[common.Hash]common.Hash),
|
||||
Account: Account{
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
Root: emptyRoot,
|
||||
CodeHash: emptyCodeHash,
|
||||
},
|
||||
}
|
||||
newBalance := big.NewInt(int64(i))
|
||||
state.SetBalance(addr, newBalance)
|
||||
modifiedAccount.Account.Balance = newBalance
|
||||
|
||||
if i%2 == 0 {
|
||||
storageKey := common.BytesToHash([]byte{i, i, i})
|
||||
storageValue := common.BytesToHash([]byte{i, i, i, i})
|
||||
modifiedAccount.Storage[storageKey] = storageValue
|
||||
|
||||
state.SetState(addr, storageKey, storageValue)
|
||||
}
|
||||
|
||||
// Collect modified accounts to assert against
|
||||
expectedStateChangesOne[addr] = modifiedAccount
|
||||
}
|
||||
|
||||
_, actualStateChangesOne, commitErr := state.Commit(false)
|
||||
if commitErr != nil {
|
||||
t.Errorf("error committing to statedb")
|
||||
}
|
||||
|
||||
assertStateChanges(actualStateChangesOne, expectedStateChangesOne, t)
|
||||
|
||||
// Commit 2: Update existing account storage
|
||||
expectedStateChangesTwo := make(StateChanges)
|
||||
|
||||
var counter int
|
||||
for addr, account := range expectedStateChangesOne {
|
||||
// Only update some of the account storage
|
||||
if counter%2 == 0 {
|
||||
newBalance := big.NewInt(100)
|
||||
account.Balance = newBalance
|
||||
state.AddBalance(addr, newBalance)
|
||||
|
||||
for storageKey := range account.Storage {
|
||||
updatedStorageValue := common.BytesToHash([]byte{0})
|
||||
|
||||
// Collect modified accounts to assert against
|
||||
account.Storage[storageKey] = updatedStorageValue
|
||||
expectedStateChangesTwo[addr] = account
|
||||
|
||||
state.SetState(addr, storageKey, updatedStorageValue)
|
||||
}
|
||||
}
|
||||
counter++
|
||||
}
|
||||
|
||||
_, stateChangesTwo, commitTwoErr := state.Commit(false)
|
||||
if commitTwoErr != nil {
|
||||
t.Errorf("error committing to statedb")
|
||||
}
|
||||
|
||||
assertStateChanges(stateChangesTwo, expectedStateChangesTwo, t)
|
||||
}
|
||||
|
||||
func assertStateChanges(actualChanges, expectedChanges StateChanges, t *testing.T) {
|
||||
if len(actualChanges) != len(expectedChanges) {
|
||||
t.Error("Test failure:", t.Name())
|
||||
t.Logf("Mismatch in number of StateChanges.ModifiedAccounts. actual: %v, expected: %v", len(actualChanges), len(expectedChanges))
|
||||
}
|
||||
|
||||
for addr, account := range actualChanges {
|
||||
expected := expectedChanges[addr]
|
||||
|
||||
if !reflect.DeepEqual(account.Nonce, expected.Nonce) {
|
||||
t.Error("Test failure:", t.Name())
|
||||
t.Errorf("Account Nonce does not match expected. actual: %v, expected: %v", account.Nonce, expected.Nonce)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(account.Balance, expected.Balance) {
|
||||
t.Error("Test failure:", t.Name())
|
||||
t.Errorf("Account Balance does not match expected. actual: %v, expected: %v", account.Balance, expected.Balance)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(account.CodeHash, expected.CodeHash) {
|
||||
t.Error("Test failure:", t.Name())
|
||||
t.Errorf("Account CodeHash does not match expected. actual: %v, expected: %v", account.CodeHash, expected.CodeHash)
|
||||
}
|
||||
|
||||
// Not asserting on the Account's Root because it's not easy to get the root between `SetState` calls
|
||||
|
||||
if !reflect.DeepEqual(account.Storage, expected.Storage) {
|
||||
t.Error("Test failure:", t.Name())
|
||||
t.Errorf("Account Storage does not match expected. actual: %v, expected: %v", account.Storage, expected.Storage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Tests that no intermediate state of an object is stored into the database,
|
||||
// only the one right before the commit.
|
||||
func TestIntermediateLeaks(t *testing.T) {
|
||||
@ -102,7 +210,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Commit and cross check the databases.
|
||||
transRoot, err := transState.Commit(false)
|
||||
transRoot, _, err := transState.Commit(false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit transition state: %v", err)
|
||||
}
|
||||
@ -110,7 +218,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
|
||||
}
|
||||
|
||||
finalRoot, err := finalState.Commit(false)
|
||||
finalRoot, _, err := finalState.Commit(false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
@ -459,7 +567,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
func TestTouchDelete(t *testing.T) {
|
||||
s := newStateTest()
|
||||
s.state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := s.state.Commit(false)
|
||||
root, _, _ := s.state.Commit(false)
|
||||
s.state.Reset(root)
|
||||
|
||||
snapshot := s.state.Snapshot()
|
||||
@ -661,7 +769,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||
addr := toAddr([]byte("so"))
|
||||
state.SetBalance(addr, big.NewInt(1))
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
// Simulate self-destructing in one transaction, then create-reverting in another
|
||||
@ -673,7 +781,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||
state.RevertToSnapshot(id)
|
||||
|
||||
// Commit the entire state and make sure we don't crash and have the correct state
|
||||
root, _ = state.Commit(true)
|
||||
root, _, _ = state.Commit(true)
|
||||
state.Reset(root)
|
||||
|
||||
if state.getStateObject(addr) != nil {
|
||||
@ -698,7 +806,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
||||
a2 := toAddr([]byte("another"))
|
||||
state.SetBalance(a2, big.NewInt(100))
|
||||
state.SetCode(a2, []byte{1, 2, 4})
|
||||
root, _ = state.Commit(false)
|
||||
root, _, _ = state.Commit(false)
|
||||
t.Logf("root: %x", root)
|
||||
// force-flush
|
||||
state.Database().TrieDB().Cap(0)
|
||||
@ -722,7 +830,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
||||
}
|
||||
// Modify the state
|
||||
state.SetBalance(addr, big.NewInt(2))
|
||||
root, err := state.Commit(false)
|
||||
root, err, _ := state.Commit(false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got root :%x", root)
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||
state.updateStateObject(obj)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.Commit(false)
|
||||
root, _, _ := state.Commit(false)
|
||||
|
||||
// Return the generated state
|
||||
return db, root, accounts
|
||||
|
||||
@ -295,7 +295,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number()))
|
||||
root, _, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
@ -681,7 +681,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
||||
return nil, fmt.Errorf("processing block %d failed: %v", block.NumberU64(), err)
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number()))
|
||||
root, _, err := statedb.Commit(api.eth.blockchain.Config().IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo
|
||||
}
|
||||
}
|
||||
// Commit and re-open to start with a clean state.
|
||||
root, _ := statedb.Commit(false)
|
||||
root, _, _ := statedb.Commit(false)
|
||||
|
||||
var snaps *snapshot.Tree
|
||||
if snapshotter {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user