Unverified Commit ddadc3d2 authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #21047 from holiman/improve_updates_2

core: improve trie updates (part 2)
parents 81bf9f97 42f9f1f0
...@@ -125,10 +125,9 @@ func (b *SimulatedBackend) Rollback() { ...@@ -125,10 +125,9 @@ func (b *SimulatedBackend) Rollback() {
func (b *SimulatedBackend) rollback() { func (b *SimulatedBackend) rollback() {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
} }
// stateByBlockNumber retrieves a state by a given blocknumber. // stateByBlockNumber retrieves a state by a given blocknumber.
......
...@@ -203,7 +203,7 @@ type BlockChain struct { ...@@ -203,7 +203,7 @@ type BlockChain struct {
engine consensus.Engine engine consensus.Engine
validator Validator // Block and state validator interface validator Validator // Block and state validator interface
prefetcher Prefetcher // Block state prefetcher interface prefetcher Prefetcher
processor Processor // Block transaction processor interface processor Processor // Block transaction processor interface
vmConfig vm.Config vmConfig vm.Config
...@@ -1860,12 +1860,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er ...@@ -1860,12 +1860,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
if err != nil { if err != nil {
return it.index, err return it.index, err
} }
// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain")
defer statedb.StopPrefetcher() // stopped on write anyway, defer meant to catch early error returns
// If we have a followup block, run that against the current state to pre-cache // If we have a followup block, run that against the current state to pre-cache
// transactions and probabilistically some of the account/storage trie nodes. // transactions and probabilistically some of the account/storage trie nodes.
var followupInterrupt uint32 var followupInterrupt uint32
if !bc.cacheConfig.TrieCleanNoPrefetch { if !bc.cacheConfig.TrieCleanNoPrefetch {
if followup, err := it.peek(); followup != nil && err == nil { if followup, err := it.peek(); followup != nil && err == nil {
throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps)
go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) { go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) {
bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt)
...@@ -1891,7 +1896,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er ...@@ -1891,7 +1896,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete, we can mark them
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete, we can mark them
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete, we can mark them
triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation triehash := statedb.AccountHashes + statedb.StorageHashes // Save to not double count in validation
trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates
trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates
...@@ -1920,7 +1924,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er ...@@ -1920,7 +1924,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
if err != nil { if err != nil {
return it.index, err return it.index, err
} }
// Update the metrics touched during block commit // Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
......
...@@ -129,12 +129,20 @@ type cachingDB struct { ...@@ -129,12 +129,20 @@ type cachingDB struct {
// OpenTrie opens the main account trie at a specific root hash. // OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db) tr, err := trie.NewSecure(root, db.db)
if err != nil {
return nil, err
}
return tr, nil
} }
// OpenStorageTrie opens the storage trie of an account. // OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db) tr, err := trie.NewSecure(root, db.db)
if err != nil {
return nil, err
}
return tr, nil
} }
// CopyTrie returns an independent copy of the given trie. // CopyTrie returns an independent copy of the given trie.
......
...@@ -156,6 +156,14 @@ func (s *stateObject) touch() { ...@@ -156,6 +156,14 @@ func (s *stateObject) touch() {
} }
func (s *stateObject) getTrie(db Database) Trie { func (s *stateObject) getTrie(db Database) Trie {
if s.trie == nil {
// Try fetching from prefetcher first
// We don't prefetch empty tries
if s.data.Root != emptyRoot && s.db.prefetcher != nil {
// When the miner is creating the pending state, there is no
// prefetcher
s.trie = s.db.prefetcher.trie(s.data.Root)
}
if s.trie == nil { if s.trie == nil {
var err error var err error
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
...@@ -164,6 +172,7 @@ func (s *stateObject) getTrie(db Database) Trie { ...@@ -164,6 +172,7 @@ func (s *stateObject) getTrie(db Database) Trie {
s.setError(fmt.Errorf("can't create storage trie: %v", err)) s.setError(fmt.Errorf("can't create storage trie: %v", err))
} }
} }
}
return s.trie return s.trie
} }
...@@ -199,10 +208,22 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has ...@@ -199,10 +208,22 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
var ( var (
enc []byte enc []byte
err error err error
meter *time.Duration
) )
readStart := time.Now()
if metrics.EnabledExpensive {
// If the snap is 'under construction', the first lookup may fail. If that
// happens, we don't want to double-count the time elapsed. Thus this
// dance with the metering.
defer func() {
if meter != nil {
*meter += time.Since(readStart)
}
}()
}
if s.db.snap != nil { if s.db.snap != nil {
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.SnapshotStorageReads += time.Since(start) }(time.Now()) meter = &s.db.SnapshotStorageReads
} }
// If the object was destructed in *this* block (and potentially resurrected), // If the object was destructed in *this* block (and potentially resurrected),
// the storage has been cleared out, and we should *not* consult the previous // the storage has been cleared out, and we should *not* consult the previous
...@@ -217,8 +238,14 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has ...@@ -217,8 +238,14 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
} }
// If snapshot unavailable or reading from it failed, load from the database // If snapshot unavailable or reading from it failed, load from the database
if s.db.snap == nil || err != nil { if s.db.snap == nil || err != nil {
if meter != nil {
// If we already spent time checking the snapshot, account for it
// and reset the readStart
*meter += time.Since(readStart)
readStart = time.Now()
}
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) meter = &s.db.StorageReads
} }
if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil { if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil {
s.setError(err) s.setError(err)
...@@ -282,9 +309,16 @@ func (s *stateObject) setState(key, value common.Hash) { ...@@ -282,9 +309,16 @@ func (s *stateObject) setState(key, value common.Hash) {
// finalise moves all dirty storage slots into the pending area to be hashed or // finalise moves all dirty storage slots into the pending area to be hashed or
// committed later. It is invoked at the end of every transaction. // committed later. It is invoked at the end of every transaction.
func (s *stateObject) finalise() { func (s *stateObject) finalise(prefetch bool) {
slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage))
for key, value := range s.dirtyStorage { for key, value := range s.dirtyStorage {
s.pendingStorage[key] = value s.pendingStorage[key] = value
if value != s.originStorage[key] {
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
}
}
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot {
s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch)
} }
if len(s.dirtyStorage) > 0 { if len(s.dirtyStorage) > 0 {
s.dirtyStorage = make(Storage) s.dirtyStorage = make(Storage)
...@@ -295,7 +329,7 @@ func (s *stateObject) finalise() { ...@@ -295,7 +329,7 @@ func (s *stateObject) finalise() {
// It will return nil if the trie has not been loaded and no changes have been made // It will return nil if the trie has not been loaded and no changes have been made
func (s *stateObject) updateTrie(db Database) Trie { func (s *stateObject) updateTrie(db Database) Trie {
// Make sure all dirty slots are finalized into the pending storage area // Make sure all dirty slots are finalized into the pending storage area
s.finalise() s.finalise(false) // Don't prefetch any more, pull directly if need be
if len(s.pendingStorage) == 0 { if len(s.pendingStorage) == 0 {
return s.trie return s.trie
} }
...@@ -303,18 +337,13 @@ func (s *stateObject) updateTrie(db Database) Trie { ...@@ -303,18 +337,13 @@ func (s *stateObject) updateTrie(db Database) Trie {
if metrics.EnabledExpensive { if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
} }
// Retrieve the snapshot storage map for the object // The snapshot storage map for the object
var storage map[common.Hash][]byte var storage map[common.Hash][]byte
if s.db.snap != nil {
// Retrieve the old storage map, if available, create a new one otherwise
storage = s.db.snapStorage[s.addrHash]
if storage == nil {
storage = make(map[common.Hash][]byte)
s.db.snapStorage[s.addrHash] = storage
}
}
// Insert all the pending updates into the trie // Insert all the pending updates into the trie
tr := s.getTrie(db) tr := s.getTrie(db)
hasher := s.db.hasher
usedStorage := make([][]byte, 0, len(s.pendingStorage))
for key, value := range s.pendingStorage { for key, value := range s.pendingStorage {
// Skip noop changes, persist actual changes // Skip noop changes, persist actual changes
if value == s.originStorage[key] { if value == s.originStorage[key] {
...@@ -331,9 +360,20 @@ func (s *stateObject) updateTrie(db Database) Trie { ...@@ -331,9 +360,20 @@ func (s *stateObject) updateTrie(db Database) Trie {
s.setError(tr.TryUpdate(key[:], v)) s.setError(tr.TryUpdate(key[:], v))
} }
// If state snapshotting is active, cache the data til commit // If state snapshotting is active, cache the data til commit
if storage != nil { if s.db.snap != nil {
storage[crypto.Keccak256Hash(key[:])] = v // v will be nil if value is 0x00 if storage == nil {
// Retrieve the old storage map, if available, create a new one otherwise
if storage = s.db.snapStorage[s.addrHash]; storage == nil {
storage = make(map[common.Hash][]byte)
s.db.snapStorage[s.addrHash] = storage
}
}
storage[crypto.HashData(hasher, key[:])] = v // v will be nil if value is 0x00
}
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
} }
if s.db.prefetcher != nil {
s.db.prefetcher.used(s.data.Root, usedStorage)
} }
if len(s.pendingStorage) > 0 { if len(s.pendingStorage) > 0 {
s.pendingStorage = make(Storage) s.pendingStorage = make(Storage)
......
...@@ -170,7 +170,7 @@ func TestSnapshot2(t *testing.T) { ...@@ -170,7 +170,7 @@ func TestSnapshot2(t *testing.T) {
state.setStateObject(so0) state.setStateObject(so0)
root, _ := state.Commit(false) root, _ := state.Commit(false)
state.Reset(root) state, _ = New(root, state.db, state.snaps)
// and one with deleted == true // and one with deleted == true
so1 := state.getStateObject(stateobjaddr1) so1 := state.getStateObject(stateobjaddr1)
......
...@@ -63,7 +63,10 @@ func (n *proofList) Delete(key []byte) error { ...@@ -63,7 +63,10 @@ func (n *proofList) Delete(key []byte) error {
// * Accounts // * Accounts
type StateDB struct { type StateDB struct {
db Database db Database
prefetcher *triePrefetcher
originalRoot common.Hash // The pre-state root, before any changes were made
trie Trie trie Trie
hasher crypto.KeccakState
snaps *snapshot.Tree snaps *snapshot.Tree
snap snapshot.Snapshot snap snapshot.Snapshot
...@@ -125,6 +128,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) ...@@ -125,6 +128,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
sdb := &StateDB{ sdb := &StateDB{
db: db, db: db,
trie: tr, trie: tr,
originalRoot: root,
snaps: snaps, snaps: snaps,
stateObjects: make(map[common.Address]*stateObject), stateObjects: make(map[common.Address]*stateObject),
stateObjectsPending: make(map[common.Address]struct{}), stateObjectsPending: make(map[common.Address]struct{}),
...@@ -133,6 +137,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) ...@@ -133,6 +137,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
preimages: make(map[common.Hash][]byte), preimages: make(map[common.Hash][]byte),
journal: newJournal(), journal: newJournal(),
accessList: newAccessList(), accessList: newAccessList(),
hasher: crypto.NewKeccakState(),
} }
if sdb.snaps != nil { if sdb.snaps != nil {
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil { if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
...@@ -144,6 +149,28 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) ...@@ -144,6 +149,28 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
return sdb, nil return sdb, nil
} }
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
// state trie concurrently while the state is mutated so that when we reach the
// commit phase, most of the needed data is already hot.
func (s *StateDB) StartPrefetcher(namespace string) {
if s.prefetcher != nil {
s.prefetcher.close()
s.prefetcher = nil
}
if s.snap != nil {
s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace)
}
}
// StopPrefetcher terminates a running prefetcher and reports any leftover stats
// from the gathered metrics.
func (s *StateDB) StopPrefetcher() {
if s.prefetcher != nil {
s.prefetcher.close()
s.prefetcher = nil
}
}
// setError remembers the first non-nil error it is called with. // setError remembers the first non-nil error it is called with.
func (s *StateDB) setError(err error) { func (s *StateDB) setError(err error) {
if s.dbErr == nil { if s.dbErr == nil {
...@@ -155,37 +182,6 @@ func (s *StateDB) Error() error { ...@@ -155,37 +182,6 @@ func (s *StateDB) Error() error {
return s.dbErr return s.dbErr
} }
// Reset clears out all ephemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
func (s *StateDB) Reset(root common.Hash) error {
tr, err := s.db.OpenTrie(root)
if err != nil {
return err
}
s.trie = tr
s.stateObjects = make(map[common.Address]*stateObject)
s.stateObjectsPending = make(map[common.Address]struct{})
s.stateObjectsDirty = make(map[common.Address]struct{})
s.thash = common.Hash{}
s.bhash = common.Hash{}
s.txIndex = 0
s.logs = make(map[common.Hash][]*types.Log)
s.logSize = 0
s.preimages = make(map[common.Hash][]byte)
s.clearJournalAndRefund()
if s.snaps != nil {
s.snapAccounts, s.snapDestructs, s.snapStorage = nil, nil, nil
if s.snap = s.snaps.Snapshot(root); s.snap != nil {
s.snapDestructs = make(map[common.Hash]struct{})
s.snapAccounts = make(map[common.Hash][]byte)
s.snapStorage = make(map[common.Hash]map[common.Hash][]byte)
}
}
s.accessList = newAccessList()
return nil
}
func (s *StateDB) AddLog(log *types.Log) { func (s *StateDB) AddLog(log *types.Log) {
s.journal.append(addLogChange{txhash: s.thash}) s.journal.append(addLogChange{txhash: s.thash})
...@@ -532,7 +528,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { ...@@ -532,7 +528,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now()) defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now())
} }
var acc *snapshot.Account var acc *snapshot.Account
if acc, err = s.snap.Account(crypto.Keccak256Hash(addr.Bytes())); err == nil { if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil {
if acc == nil { if acc == nil {
return nil return nil
} }
...@@ -675,6 +671,7 @@ func (s *StateDB) Copy() *StateDB { ...@@ -675,6 +671,7 @@ func (s *StateDB) Copy() *StateDB {
logSize: s.logSize, logSize: s.logSize,
preimages: make(map[common.Hash][]byte, len(s.preimages)), preimages: make(map[common.Hash][]byte, len(s.preimages)),
journal: newJournal(), journal: newJournal(),
hasher: crypto.NewKeccakState(),
} }
// Copy the dirty states, logs, and preimages // Copy the dirty states, logs, and preimages
for addr := range s.journal.dirties { for addr := range s.journal.dirties {
...@@ -724,6 +721,13 @@ func (s *StateDB) Copy() *StateDB { ...@@ -724,6 +721,13 @@ func (s *StateDB) Copy() *StateDB {
// However, it doesn't cost us much to copy an empty list, so we do it anyway // However, it doesn't cost us much to copy an empty list, so we do it anyway
// to not blow up if we ever decide copy it in the middle of a transaction // to not blow up if we ever decide copy it in the middle of a transaction
state.accessList = s.accessList.Copy() state.accessList = s.accessList.Copy()
// If there's a prefetcher running, make an inactive copy of it that can
// only access data but does not actively preload (since the user will not
// know that they need to explicitly terminate an active copy).
if s.prefetcher != nil {
state.prefetcher = s.prefetcher.copy()
}
return state return state
} }
...@@ -760,6 +764,7 @@ func (s *StateDB) GetRefund() uint64 { ...@@ -760,6 +764,7 @@ func (s *StateDB) GetRefund() uint64 {
// the journal as well as the refunds. Finalise, however, will not push any updates // the journal as well as the refunds. Finalise, however, will not push any updates
// into the tries just yet. Only IntermediateRoot or Commit will do that. // into the tries just yet. Only IntermediateRoot or Commit will do that.
func (s *StateDB) Finalise(deleteEmptyObjects bool) { func (s *StateDB) Finalise(deleteEmptyObjects bool) {
addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties))
for addr := range s.journal.dirties { for addr := range s.journal.dirties {
obj, exist := s.stateObjects[addr] obj, exist := s.stateObjects[addr]
if !exist { if !exist {
...@@ -784,10 +789,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { ...@@ -784,10 +789,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a ressurrect) delete(s.snapStorage, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a ressurrect)
} }
} else { } else {
obj.finalise() obj.finalise(true) // Prefetch slots in the background
} }
s.stateObjectsPending[addr] = struct{}{} s.stateObjectsPending[addr] = struct{}{}
s.stateObjectsDirty[addr] = struct{}{} s.stateObjectsDirty[addr] = struct{}{}
// At this point, also ship the address off to the precacher. The precacher
// will start loading tries, and when the change is eventually committed,
// the commit-phase will be a lot faster
addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure
}
if s.prefetcher != nil && len(addressesToPrefetch) > 0 {
s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch)
} }
// Invalidate journal because reverting across transactions is not allowed. // Invalidate journal because reverting across transactions is not allowed.
s.clearJournalAndRefund() s.clearJournalAndRefund()
...@@ -800,14 +813,49 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { ...@@ -800,14 +813,49 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Finalise all the dirty storage states and write them into the tries // Finalise all the dirty storage states and write them into the tries
s.Finalise(deleteEmptyObjects) s.Finalise(deleteEmptyObjects)
// If there was a trie prefetcher operating, it gets aborted and irrevocably
// modified after we start retrieving tries. Remove it from the statedb after
// this round of use.
//
// This is weird pre-byzantium since the first tx runs with a prefetcher and
// the remainder without, but pre-byzantium even the initial prefetcher is
// useless, so no sleep lost.
prefetcher := s.prefetcher
if s.prefetcher != nil {
defer func() {
s.prefetcher.close()
s.prefetcher = nil
}()
}
// Although naively it makes sense to retrieve the account trie and then do
// the contract storage and account updates sequentially, that short circuits
// the account prefetcher. Instead, let's process all the storage updates
// first, giving the account prefeches just a few more milliseconds of time
// to pull useful data from disk.
for addr := range s.stateObjectsPending { for addr := range s.stateObjectsPending {
obj := s.stateObjects[addr] if obj := s.stateObjects[addr]; !obj.deleted {
if obj.deleted { obj.updateRoot(s.db)
}
}
// Now we're about to start to write changes to the trie. The trie is so far
// _untouched_. We can check with the prefetcher, if it can give us a trie
// which has the same root, but also has some content loaded into it.
if prefetcher != nil {
if trie := prefetcher.trie(s.originalRoot); trie != nil {
s.trie = trie
}
}
usedAddrs := make([][]byte, 0, len(s.stateObjectsPending))
for addr := range s.stateObjectsPending {
if obj := s.stateObjects[addr]; obj.deleted {
s.deleteStateObject(obj) s.deleteStateObject(obj)
} else { } else {
obj.updateRoot(s.db)
s.updateStateObject(obj) s.updateStateObject(obj)
} }
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
}
if prefetcher != nil {
prefetcher.used(s.originalRoot, usedAddrs)
} }
if len(s.stateObjectsPending) > 0 { if len(s.stateObjectsPending) > 0 {
s.stateObjectsPending = make(map[common.Address]struct{}) s.stateObjectsPending = make(map[common.Address]struct{})
......
...@@ -474,7 +474,7 @@ func TestTouchDelete(t *testing.T) { ...@@ -474,7 +474,7 @@ func TestTouchDelete(t *testing.T) {
s := newStateTest() s := newStateTest()
s.state.GetOrNewStateObject(common.Address{}) s.state.GetOrNewStateObject(common.Address{})
root, _ := s.state.Commit(false) root, _ := s.state.Commit(false)
s.state.Reset(root) s.state, _ = New(root, s.state.db, s.state.snaps)
snapshot := s.state.Snapshot() snapshot := s.state.Snapshot()
s.state.AddBalance(common.Address{}, new(big.Int)) s.state.AddBalance(common.Address{}, new(big.Int))
...@@ -676,7 +676,7 @@ func TestDeleteCreateRevert(t *testing.T) { ...@@ -676,7 +676,7 @@ func TestDeleteCreateRevert(t *testing.T) {
state.SetBalance(addr, big.NewInt(1)) state.SetBalance(addr, big.NewInt(1))
root, _ := state.Commit(false) root, _ := state.Commit(false)
state.Reset(root) state, _ = New(root, state.db, state.snaps)
// Simulate self-destructing in one transaction, then create-reverting in another // Simulate self-destructing in one transaction, then create-reverting in another
state.Suicide(addr) state.Suicide(addr)
...@@ -688,7 +688,7 @@ func TestDeleteCreateRevert(t *testing.T) { ...@@ -688,7 +688,7 @@ func TestDeleteCreateRevert(t *testing.T) {
// Commit the entire state and make sure we don't crash and have the correct state // 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) state, _ = New(root, state.db, state.snaps)
if state.getStateObject(addr) != nil { if state.getStateObject(addr) != nil {
t.Fatalf("self-destructed contract came alive") t.Fatalf("self-destructed contract came alive")
......
This diff is collapsed.
...@@ -60,10 +60,23 @@ type KeccakState interface { ...@@ -60,10 +60,23 @@ type KeccakState interface {
Read([]byte) (int, error) Read([]byte) (int, error)
} }
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
return sha3.NewLegacyKeccak256().(KeccakState)
}
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
func HashData(kh KeccakState, data []byte) (h common.Hash) {
kh.Reset()
kh.Write(data)
kh.Read(h[:])
return h
}
// Keccak256 calculates and returns the Keccak256 hash of the input data. // Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte { func Keccak256(data ...[]byte) []byte {
b := make([]byte, 32) b := make([]byte, 32)
d := sha3.NewLegacyKeccak256().(KeccakState) d := NewKeccakState()
for _, b := range data { for _, b := range data {
d.Write(b) d.Write(b)
} }
...@@ -74,7 +87,7 @@ func Keccak256(data ...[]byte) []byte { ...@@ -74,7 +87,7 @@ func Keccak256(data ...[]byte) []byte {
// Keccak256Hash calculates and returns the Keccak256 hash of the input data, // Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure. // converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) { func Keccak256Hash(data ...[]byte) (h common.Hash) {
d := sha3.NewLegacyKeccak256().(KeccakState) d := NewKeccakState()
for _, b := range data { for _, b := range data {
d.Write(b) d.Write(b)
} }
......
...@@ -42,6 +42,13 @@ func TestKeccak256Hash(t *testing.T) { ...@@ -42,6 +42,13 @@ func TestKeccak256Hash(t *testing.T) {
checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp) checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp)
} }
func TestKeccak256Hasher(t *testing.T) {
msg := []byte("abc")
exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
hasher := NewKeccakState()
checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := HashData(hasher, in); return h[:] }, msg, exp)
}
func TestToECDSAErrors(t *testing.T) { func TestToECDSAErrors(t *testing.T) {
if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil { if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil {
t.Fatal("HexToECDSA should've returned error") t.Fatal("HexToECDSA should've returned error")
......
...@@ -299,7 +299,8 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl ...@@ -299,7 +299,8 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
failed = err failed = err
break break
} }
if err := statedb.Reset(root); err != nil { statedb, err = state.New(root, database, nil)
if err != nil {
failed = err failed = err
break break
} }
...@@ -699,7 +700,8 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (* ...@@ -699,7 +700,8 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := statedb.Reset(root); err != nil { statedb, err = state.New(root, database, nil)
if err != nil {
return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err) return nil, fmt.Errorf("state reset after block %d failed: %v", block.NumberU64(), err)
} }
database.TrieDB().Reference(root, common.Hash{}) database.TrieDB().Reference(root, common.Hash{})
......
...@@ -303,6 +303,9 @@ func (w *worker) isRunning() bool { ...@@ -303,6 +303,9 @@ func (w *worker) isRunning() bool {
// close terminates all background threads maintained by the worker. // close terminates all background threads maintained by the worker.
// Note the worker does not support being closed multiple times. // Note the worker does not support being closed multiple times.
func (w *worker) close() { func (w *worker) close() {
if w.current != nil && w.current.state != nil {
w.current.state.StopPrefetcher()
}
atomic.StoreInt32(&w.running, 0) atomic.StoreInt32(&w.running, 0)
close(w.exitCh) close(w.exitCh)
} }
...@@ -642,10 +645,14 @@ func (w *worker) resultLoop() { ...@@ -642,10 +645,14 @@ func (w *worker) resultLoop() {
// makeCurrent creates a new environment for the current cycle. // makeCurrent creates a new environment for the current cycle.
func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
// Retrieve the parent state to execute on top and start a prefetcher for
// the miner to speed block sealing up a bit
state, err := w.chain.StateAt(parent.Root()) state, err := w.chain.StateAt(parent.Root())
if err != nil { if err != nil {
return err return err
} }
state.StartPrefetcher("miner")
env := &environment{ env := &environment{
signer: types.NewEIP155Signer(w.chainConfig.ChainID), signer: types.NewEIP155Signer(w.chainConfig.ChainID),
state: state, state: state,
...@@ -654,7 +661,6 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { ...@@ -654,7 +661,6 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
uncles: mapset.NewSet(), uncles: mapset.NewSet(),
header: header, header: header,
} }
// when 08 is processed ancestors contain 07 (quick block) // when 08 is processed ancestors contain 07 (quick block)
for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {
...@@ -663,9 +669,14 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { ...@@ -663,9 +669,14 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
env.family.Add(ancestor.Hash()) env.family.Add(ancestor.Hash())
env.ancestors.Add(ancestor.Hash()) env.ancestors.Add(ancestor.Hash())
} }
// Keep track of transactions which return errors so they can be removed // Keep track of transactions which return errors so they can be removed
env.tcount = 0 env.tcount = 0
// Swap out the old work with the new one, terminating any leftover prefetcher
// processes in the mean time and starting a new one.
if w.current != nil && w.current.state != nil {
w.current.state.StopPrefetcher()
}
w.current = env w.current = env
return nil return nil
} }
...@@ -719,7 +730,6 @@ func (w *worker) updateSnapshot() { ...@@ -719,7 +730,6 @@ func (w *worker) updateSnapshot() {
w.current.receipts, w.current.receipts,
new(trie.Trie), new(trie.Trie),
) )
w.snapshotState = w.current.state.Copy() w.snapshotState = w.current.state.Copy()
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment