mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 20:56:42 +00:00
trie: make value node resolvable, not needing to be known at insertion time.
This commit is contained in:
parent
078a5ecb7d
commit
a868a8e1e0
13 changed files with 116 additions and 41 deletions
|
|
@ -99,6 +99,8 @@ type Trie interface {
|
|||
// in the trie with provided address.
|
||||
UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error
|
||||
|
||||
UpdateAccountAsync(address common.Address, accountResolver func() *types.StateAccount) error
|
||||
|
||||
// UpdateStorage associates key with value in the trie. If value has length zero,
|
||||
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||
// by the caller while they are stored in the trie. If a node was not found in the
|
||||
|
|
|
|||
|
|
@ -577,6 +577,13 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
|
|||
}
|
||||
}
|
||||
|
||||
// updateStateObject writes the given object to the trie.
|
||||
func (s *StateDB) updateStateObjectAsync(addr common.Address, resolver func() *types.StateAccount) {
|
||||
if err := s.trie.UpdateAccountAsync(addr, resolver); err != nil {
|
||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr, err))
|
||||
}
|
||||
}
|
||||
|
||||
// deleteStateObject removes the given object from the state trie.
|
||||
func (s *StateDB) deleteStateObject(addr common.Address) {
|
||||
if err := s.trie.DeleteAccount(addr); err != nil {
|
||||
|
|
@ -829,11 +836,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
// later time.
|
||||
workers.SetLimit(1)
|
||||
}
|
||||
|
||||
stateObjectsResolve := make(map[common.Address]func() *types.StateAccount)
|
||||
for addr, op := range s.mutations {
|
||||
if op.applied || op.isDelete() {
|
||||
continue
|
||||
}
|
||||
obj := s.stateObjects[addr] // closure for the task runner below
|
||||
complete := make(chan *types.StateAccount)
|
||||
workers.Go(func() error {
|
||||
if s.db.TrieDB().IsVerkle() {
|
||||
obj.updateTrie()
|
||||
|
|
@ -846,8 +856,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
s.witness.AddState(obj.trie.Witness())
|
||||
}
|
||||
}
|
||||
complete <- &obj.data
|
||||
return nil
|
||||
})
|
||||
|
||||
stateObjectsResolve[addr] = func() *types.StateAccount {
|
||||
return <-complete
|
||||
}
|
||||
}
|
||||
// If witness building is enabled, gather all the read-only accesses.
|
||||
// Skip witness collection in Verkle mode, they will be gathered
|
||||
|
|
@ -898,7 +913,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
}
|
||||
}
|
||||
}
|
||||
workers.Wait()
|
||||
s.StorageUpdates += time.Since(start)
|
||||
|
||||
// Now we're about to start to write changes to the trie. The trie is so far
|
||||
|
|
@ -939,7 +953,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
if op.isDelete() {
|
||||
deletedAddrs = append(deletedAddrs, addr)
|
||||
} else {
|
||||
s.updateStateObject(s.stateObjects[addr])
|
||||
if s.db.TrieDB().IsVerkle() {
|
||||
s.updateStateObject(s.stateObjects[addr])
|
||||
} else {
|
||||
s.updateStateObjectAsync(addr, stateObjectsResolve[addr])
|
||||
}
|
||||
s.AccountUpdated += 1
|
||||
}
|
||||
usedAddrs = append(usedAddrs, addr) // Copy needed for closure
|
||||
|
|
@ -966,6 +984,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
s.witnessStats.Add(witness, common.Hash{})
|
||||
}
|
||||
}
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,8 +157,8 @@ func (c *committer) store(path []byte, n node) node {
|
|||
// length of leaves should be exactly same.
|
||||
if c.collectLeaf {
|
||||
if sn, ok := n.(*shortNode); ok {
|
||||
if val, ok := sn.Val.(valueNode); ok {
|
||||
c.nodes.AddLeaf(nhash, val)
|
||||
if val, ok := sn.Val.(*valueNode); ok {
|
||||
c.nodes.AddLeaf(nhash, val.resolve())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ func forGatherChildren(n node, onChild func(hash common.Hash)) {
|
|||
}
|
||||
case hashNode:
|
||||
onChild(common.BytesToHash(n))
|
||||
case valueNode, nil:
|
||||
case *valueNode, nil:
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown node type: %T", n))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ func (h *hasher) encodeShortNode(n *shortNode) []byte {
|
|||
if hasTerm(n.Key) {
|
||||
var ln leafNodeEncoder
|
||||
ln.Key = hexToCompact(n.Key)
|
||||
ln.Val = n.Val.(valueNode)
|
||||
ln.Val = n.Val.(*valueNode).resolve()
|
||||
ln.encode(h.encbuf)
|
||||
return h.encodedBytes()
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ func (h *hasher) encodeFullNode(n *fullNode) []byte {
|
|||
}
|
||||
}
|
||||
if n.Children[16] != nil {
|
||||
fn.Children[16] = n.Children[16].(valueNode)
|
||||
fn.Children[16] = n.Children[16].(*valueNode).resolve()
|
||||
}
|
||||
fn.encode(h.encbuf)
|
||||
fnEncoderPool.Put(fn)
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ func (it *nodeIterator) Leaf() bool {
|
|||
|
||||
func (it *nodeIterator) LeafKey() []byte {
|
||||
if len(it.stack) > 0 {
|
||||
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||
if _, ok := it.stack[len(it.stack)-1].node.(*valueNode); ok {
|
||||
return hexToKeybytes(it.path)
|
||||
}
|
||||
}
|
||||
|
|
@ -224,8 +224,8 @@ func (it *nodeIterator) LeafKey() []byte {
|
|||
|
||||
func (it *nodeIterator) LeafBlob() []byte {
|
||||
if len(it.stack) > 0 {
|
||||
if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||
return node
|
||||
if node, ok := it.stack[len(it.stack)-1].node.(*valueNode); ok {
|
||||
return node.resolve()
|
||||
}
|
||||
}
|
||||
panic("not at leaf")
|
||||
|
|
@ -233,7 +233,7 @@ func (it *nodeIterator) LeafBlob() []byte {
|
|||
|
||||
func (it *nodeIterator) LeafProof() [][]byte {
|
||||
if len(it.stack) > 0 {
|
||||
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
||||
if _, ok := it.stack[len(it.stack)-1].node.(*valueNode); ok {
|
||||
hasher := newHasher(false)
|
||||
defer returnHasherToPool(hasher)
|
||||
proofs := make([][]byte, 0, len(it.stack))
|
||||
|
|
|
|||
30
trie/node.go
30
trie/node.go
|
|
@ -44,7 +44,10 @@ type (
|
|||
flags nodeFlag
|
||||
}
|
||||
hashNode []byte
|
||||
valueNode []byte
|
||||
valueNode struct {
|
||||
resolver func() []byte
|
||||
val []byte
|
||||
}
|
||||
|
||||
// fullnodeEncoder is a type used exclusively for encoding fullNode.
|
||||
// Briefly instantiating a fullnodeEncoder and initializing with
|
||||
|
|
@ -68,6 +71,19 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func newValueNode(resolver func() []byte) *valueNode {
|
||||
return &valueNode{
|
||||
resolver: resolver,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *valueNode) resolve() []byte {
|
||||
if v.val == nil {
|
||||
v.val = v.resolver()
|
||||
}
|
||||
return v.val
|
||||
}
|
||||
|
||||
// EncodeRLP encodes a full node into the consensus RLP format.
|
||||
func (n *fullNode) EncodeRLP(w io.Writer) error {
|
||||
eb := rlp.NewEncoderBuffer(w)
|
||||
|
|
@ -91,13 +107,13 @@ func (n nodeFlag) copy() nodeFlag {
|
|||
func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
|
||||
func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
|
||||
func (n hashNode) cache() (hashNode, bool) { return nil, true }
|
||||
func (n valueNode) cache() (hashNode, bool) { return nil, true }
|
||||
func (n *valueNode) cache() (hashNode, bool) { return nil, true }
|
||||
|
||||
// Pretty printing.
|
||||
func (n *fullNode) String() string { return n.fstring("") }
|
||||
func (n *shortNode) String() string { return n.fstring("") }
|
||||
func (n hashNode) String() string { return n.fstring("") }
|
||||
func (n valueNode) String() string { return n.fstring("") }
|
||||
func (n *valueNode) String() string { return n.fstring("") }
|
||||
|
||||
func (n *fullNode) fstring(ind string) string {
|
||||
resp := fmt.Sprintf("[\n%s ", ind)
|
||||
|
|
@ -117,8 +133,8 @@ func (n *shortNode) fstring(ind string) string {
|
|||
func (n hashNode) fstring(ind string) string {
|
||||
return fmt.Sprintf("<%x> ", []byte(n))
|
||||
}
|
||||
func (n valueNode) fstring(ind string) string {
|
||||
return fmt.Sprintf("%x ", []byte(n))
|
||||
func (n *valueNode) fstring(ind string) string {
|
||||
return fmt.Sprintf("%x ", n.resolve())
|
||||
}
|
||||
|
||||
// mustDecodeNode is a wrapper of decodeNode and panic if any error is encountered.
|
||||
|
|
@ -185,7 +201,7 @@ func decodeShort(hash, elems []byte) (node, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value node: %v", err)
|
||||
}
|
||||
return &shortNode{key, valueNode(val), flag}, nil
|
||||
return &shortNode{key, newValueNode(func() []byte { return val }), flag}, nil
|
||||
}
|
||||
r, _, err := decodeRef(rest)
|
||||
if err != nil {
|
||||
|
|
@ -208,7 +224,7 @@ func decodeFull(hash, elems []byte) (*fullNode, error) {
|
|||
return n, err
|
||||
}
|
||||
if len(val) > 0 {
|
||||
n.Children[16] = valueNode(val)
|
||||
n.Children[16] = newValueNode(func() []byte { return val })
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,6 @@ func (n hashNode) encode(w rlp.EncoderBuffer) {
|
|||
w.WriteBytes(n)
|
||||
}
|
||||
|
||||
func (n valueNode) encode(w rlp.EncoderBuffer) {
|
||||
w.WriteBytes(n)
|
||||
func (n *valueNode) encode(w rlp.EncoderBuffer) {
|
||||
w.WriteBytes(n.resolve())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader)
|
|||
case hashNode:
|
||||
key = keyrest
|
||||
copy(wantHash[:], cld)
|
||||
case valueNode:
|
||||
return cld, nil
|
||||
case *valueNode:
|
||||
return cld.resolve(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,8 +191,8 @@ func proofToPath(rootHash common.Hash, root node, key []byte, proofDb ethdb.KeyV
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case valueNode:
|
||||
valnode = cld
|
||||
case *valueNode:
|
||||
valnode = cld.resolve()
|
||||
}
|
||||
// Link the parent and child.
|
||||
switch pnode := parent.(type) {
|
||||
|
|
@ -298,7 +298,7 @@ findFork:
|
|||
}
|
||||
// Only one proof points to non-existent key.
|
||||
if shortForkRight != 0 {
|
||||
if _, ok := rn.Val.(valueNode); ok {
|
||||
if _, ok := rn.Val.(*valueNode); ok {
|
||||
// The fork point is root node, unset the entire trie
|
||||
if parent == nil {
|
||||
return true, nil
|
||||
|
|
@ -309,7 +309,7 @@ findFork:
|
|||
return false, unset(rn, rn.Val, left[pos:], len(rn.Key), false)
|
||||
}
|
||||
if shortForkLeft != 0 {
|
||||
if _, ok := rn.Val.(valueNode); ok {
|
||||
if _, ok := rn.Val.(*valueNode); ok {
|
||||
// The fork point is root node, unset the entire trie
|
||||
if parent == nil {
|
||||
return true, nil
|
||||
|
|
@ -396,7 +396,7 @@ func unset(parent node, child node, key []byte, pos int, removeLeft bool) error
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if _, ok := cld.Val.(valueNode); ok {
|
||||
if _, ok := cld.Val.(*valueNode); ok {
|
||||
fn := parent.(*fullNode)
|
||||
fn.Children[key[pos-1]] = nil
|
||||
return nil
|
||||
|
|
@ -432,7 +432,7 @@ func hasRightElement(node node, key []byte) bool {
|
|||
return bytes.Compare(rn.Key, key[pos:]) > 0
|
||||
}
|
||||
node, pos = rn.Val, pos+len(rn.Key)
|
||||
case valueNode:
|
||||
case *valueNode:
|
||||
return false // We have resolved the whole path
|
||||
default:
|
||||
panic(fmt.Sprintf("%T: invalid node: %v", node, node)) // hashnode
|
||||
|
|
@ -612,7 +612,7 @@ func get(tn node, key []byte, skipResolved bool) ([]byte, node) {
|
|||
return key, n
|
||||
case nil:
|
||||
return key, nil
|
||||
case valueNode:
|
||||
case *valueNode:
|
||||
return nil, n
|
||||
default:
|
||||
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
||||
|
|
|
|||
|
|
@ -226,6 +226,25 @@ func (t *StateTrie) UpdateAccount(address common.Address, acc *types.StateAccoun
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *StateTrie) UpdateAccountAsync(address common.Address, accountResolve func() *types.StateAccount) error {
|
||||
hk := crypto.Keccak256(address.Bytes())
|
||||
resolve := func() []byte {
|
||||
acc := accountResolve()
|
||||
data, err := rlp.EncodeToBytes(acc)
|
||||
if err != nil {
|
||||
panic(err) // TODO: what do do here?
|
||||
}
|
||||
return data
|
||||
}
|
||||
if err := t.trie.UpdateAsync(hk, resolve); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.preimages != nil {
|
||||
t.secKeyCache[common.Hash(hk)] = address.Bytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StateTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
|
|||
for _, child := range children {
|
||||
// Notify any external watcher of a new key/value node
|
||||
if req.callback != nil {
|
||||
if node, ok := (child.node).(valueNode); ok {
|
||||
if node, ok := (child.node).(*valueNode); ok {
|
||||
var paths [][]byte
|
||||
if len(child.path) == 2*common.HashLength {
|
||||
paths = append(paths, hexToKeybytes(child.path))
|
||||
|
|
@ -620,7 +620,7 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
|
|||
paths = append(paths, hexToKeybytes(child.path[:2*common.HashLength]))
|
||||
paths = append(paths, hexToKeybytes(child.path[2*common.HashLength:]))
|
||||
}
|
||||
if err := req.callback(paths, child.path, node, req.hash, req.path); err != nil {
|
||||
if err := req.callback(paths, child.path, node.resolve(), req.hash, req.path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -138,6 +139,9 @@ func (t *TransitionTrie) UpdateAccount(addr common.Address, account *types.State
|
|||
// only needs to know what the account trie does now.
|
||||
return t.overlay.UpdateAccount(addr, account, codeLen)
|
||||
}
|
||||
func (t *TransitionTrie) UpdateAccountAsync(address common.Address, accountResolver func() *types.StateAccount) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// DeleteStorage removes any existing value for key from the trie. If a node was not
|
||||
// found in the database, a trie.MissingNodeError is returned.
|
||||
|
|
|
|||
31
trie/trie.go
31
trie/trie.go
|
|
@ -193,8 +193,8 @@ func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode no
|
|||
switch n := (origNode).(type) {
|
||||
case nil:
|
||||
return nil, nil, false, nil
|
||||
case valueNode:
|
||||
return n, n, false, nil
|
||||
case *valueNode:
|
||||
return n.resolve(), n, false, nil
|
||||
case *shortNode:
|
||||
if !bytes.HasPrefix(key[pos:], n.Key) {
|
||||
// key not found in trie
|
||||
|
|
@ -322,7 +322,7 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod
|
|||
}
|
||||
// Path still needs to be traversed, descend into children
|
||||
switch n := (origNode).(type) {
|
||||
case valueNode:
|
||||
case *valueNode:
|
||||
// Path prematurely ended, abort
|
||||
return nil, nil, 0, nil
|
||||
|
||||
|
|
@ -382,12 +382,26 @@ func (t *Trie) Update(key, value []byte) error {
|
|||
return t.update(key, value)
|
||||
}
|
||||
|
||||
func (t *Trie) UpdateAsync(key []byte, valueResolver func() []byte) error {
|
||||
t.unhashed++
|
||||
t.uncommitted++
|
||||
k := keybytesToHex(key)
|
||||
|
||||
// NOTE: this does not support deletions (the length of the value is not known until it is resolved)
|
||||
_, n, err := t.insert(t.root, nil, k, newValueNode(valueResolver))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.root = n
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Trie) update(key, value []byte) error {
|
||||
t.unhashed++
|
||||
t.uncommitted++
|
||||
k := keybytesToHex(key)
|
||||
if len(value) != 0 {
|
||||
_, n, err := t.insert(t.root, nil, k, valueNode(value))
|
||||
_, n, err := t.insert(t.root, nil, k, newValueNode(func() []byte { return value }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -404,9 +418,6 @@ func (t *Trie) update(key, value []byte) error {
|
|||
|
||||
func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) {
|
||||
if len(key) == 0 {
|
||||
if v, ok := n.(valueNode); ok {
|
||||
return !bytes.Equal(v, value.(valueNode)), value, nil
|
||||
}
|
||||
return true, value, nil
|
||||
}
|
||||
switch n := n.(type) {
|
||||
|
|
@ -616,7 +627,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
|
|||
// n still contains at least two values and cannot be reduced.
|
||||
return true, n, nil
|
||||
|
||||
case valueNode:
|
||||
case *valueNode:
|
||||
return true, nil, nil
|
||||
|
||||
case nil:
|
||||
|
|
@ -646,8 +657,8 @@ func copyNode(n node) node {
|
|||
switch n := (n).(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case valueNode:
|
||||
return valueNode(common.CopyBytes(n))
|
||||
case *valueNode:
|
||||
return newValueNode(func() []byte { return common.CopyBytes(n.resolve()) })
|
||||
|
||||
case *shortNode:
|
||||
return &shortNode{
|
||||
|
|
|
|||
|
|
@ -177,6 +177,10 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *VerkleTrie) UpdateAccountAsync(address common.Address, accountResolver func() *types.StateAccount) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// UpdateStorage implements state.Trie, writing the provided storage slot into
|
||||
// the tree. If the tree is corrupted, an error will be returned.
|
||||
func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error {
|
||||
|
|
|
|||
Loading…
Reference in a new issue