diff --git a/core/state/database.go b/core/state/database.go index ecc2c134da..c3d4b62410 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -69,7 +69,8 @@ type Trie interface { // by the caller while they are stored in the trie. If a node was not found in the // database, a trie.MissingNodeError is returned. TryUpdate(key, value []byte) error - + BatchStart() + BatchEnd() // TryDelete removes any existing value for key from the trie. If a node was not // found in the database, a trie.MissingNodeError is returned. TryDelete(key []byte) error diff --git a/core/state/statedb.go b/core/state/statedb.go index 8399f1bab9..f54fd929b2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -688,7 +688,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) - + s.trie.BatchStart() for addr := range s.stateObjectsPending { obj := s.stateObjects[addr] if obj.deleted { @@ -698,6 +698,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { s.updateStateObject(obj) } } + s.trie.BatchEnd() if len(s.stateObjectsPending) > 0 { s.stateObjectsPending = make(map[common.Address]struct{}) } diff --git a/light/trie.go b/light/trie.go index e512bf6f95..4579cc2571 100644 --- a/light/trie.go +++ b/light/trie.go @@ -112,6 +112,9 @@ func (t *odrTrie) TryUpdate(key, value []byte) error { }) } +func (t *odrTrie) BatchStart() {} +func (t *odrTrie) BatchEnd() {} + func (t *odrTrie) TryDelete(key []byte) error { key = crypto.Keccak256(key) return t.do(key, func() error { diff --git a/trie/secure_trie.go b/trie/secure_trie.go index fbc591ed10..4e385c3593 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -109,6 +109,13 @@ func (t *SecureTrie) TryUpdate(key, value []byte) error { return nil } +func (t *SecureTrie) BatchStart(){ + t.trie.batchStart() +} +func (t *SecureTrie) BatchEnd(){ + t.trie.batchEnd() +} + // Delete removes any existing value for key from the trie. func (t *SecureTrie) Delete(key []byte) { if err := t.TryDelete(key); err != nil { diff --git a/trie/trie.go b/trie/trie.go index 80e4d247f2..1c27bab5e5 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -52,6 +52,7 @@ type Trie struct { dirtyCount int // And leafs to hash unhashedCount int + batchMode bool } // newFlag returns the cache flag value for a newly created node. @@ -186,6 +187,13 @@ func (t *Trie) TryUpdate(key, value []byte) error { return nil } +func (t *Trie) batchStart(){ + t.batchMode = true +} +func (t *Trie) batchEnd(){ + t.batchMode = false +} + func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) { if len(key) == 0 { if v, ok := n.(valueNode); ok { @@ -228,7 +236,20 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error if !dirty || err != nil { return false, n, err } - n = n.copy() + // If we're in batch-mode, we don't keep 'ephemeral' changes. + // When we modify a node, we only copy it in case it is an old committed + // node. + // If the node is "new", we just update in place. + if t.batchMode{ + if h, dirty := n.cache(); !dirty || h != nil{ + // This node is either not dirty, or already hashed. We copy it + n = n.copy() + }else{ + // No copy + } + }else{ + n = n.copy() + } n.flags = t.newFlag() n.Children[key[0]] = nn return true, n, nil