From 387b0fe6e84298b6ac0625e624df95fa5be78131 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 27 Nov 2023 14:23:20 +0100 Subject: [PATCH] core, trie: experimentation with state witnesses --- core/state/database.go | 2 ++ core/state/state_object.go | 11 ++++++----- core/state/statedb.go | 11 +++++++++-- light/trie.go | 4 ++++ trie/secure_trie.go | 16 ++++++++++++++++ trie/trie.go | 14 +++++++++++++- trie/verkle.go | 4 ++++ 7 files changed, 54 insertions(+), 8 deletions(-) diff --git a/core/state/database.go b/core/state/database.go index b55f870d90..f6b0d19be7 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -125,6 +125,8 @@ type Trie interface { // be created with new root and updated trie database for following usage Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) + CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) + // NodeIterator returns an iterator that returns nodes of the trie. Iteration // starts at the key after the given start key. And error will be returned // if fails to create node iterator. diff --git a/core/state/state_object.go b/core/state/state_object.go index fc66b48114..66be5becf6 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -375,11 +375,11 @@ func (s *stateObject) updateRoot() { // commit obtains a set of dirty storage trie nodes and updates the account data. // The returned set can be nil if nothing to commit. This function assumes all // storage mutations have already been flushed into trie by updateRoot. -func (s *stateObject) commit() (*trienode.NodeSet, error) { +func (s *stateObject) commit() (*trienode.NodeSet, map[string][]byte, error) { // Short circuit if trie is not even loaded, don't bother with committing anything if s.trie == nil { s.origin = s.data.Copy() - return nil, nil + return nil, nil, nil } // Track the amount of time wasted on committing the storage trie if metrics.EnabledExpensive { @@ -388,15 +388,16 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // The trie is currently in an open state and could potentially contain // cached mutations. Call commit to acquire a set of nodes that have been // modified, the set can be nil if nothing to commit. - root, nodes, err := s.trie.Commit(false) + root, nodes, accessList, err := s.trie.CommitAndObtainAccessList(false) + //root, nodes, err := s.trie.Commit(false) if err != nil { - return nil, err + return nil, nil, err } s.data.Root = root // Update original account data after commit s.origin = s.data.Copy() - return nodes, nil + return nodes, accessList, nil } // AddBalance adds amount to s's balance. diff --git a/core/state/statedb.go b/core/state/statedb.go index 674227857c..c5e5f720d1 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1169,6 +1169,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er if s.dbErr != nil { return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } + w := newWitness(s.originalRoot) // Finalize any pending changes and merge everything into the tries s.IntermediateRoot(deleteEmptyObjects) @@ -1198,7 +1199,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er obj.dirtyCode = false } // Write any storage changes in the state object to its storage trie - set, err := obj.commit() + set, accessList, err := obj.commit() + w.addAccessList(obj.addrHash, accessList) if err != nil { return common.Hash{}, err } @@ -1224,7 +1226,9 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er if metrics.EnabledExpensive { start = time.Now() } - root, set, err := s.trie.Commit(true) + //root, set, err := s.trie.Commit(true) + root, set, accessList, err := s.trie.CommitAndObtainAccessList(true) + w.addAccessList(common.Hash{}, accessList) if err != nil { return common.Hash{}, err } @@ -1298,6 +1302,9 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.storagesOrigin = make(map[common.Address]map[common.Hash][]byte) s.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) + + w.Dump() + return root, nil } diff --git a/light/trie.go b/light/trie.go index 1d93bdf415..c14d172cb5 100644 --- a/light/trie.go +++ b/light/trie.go @@ -177,6 +177,10 @@ func (t *odrTrie) DeleteAccount(address common.Address) error { }) } +func (t *odrTrie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) { + panic("not implemented") +} + func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { if t.trie == nil { return t.id.Root, nil, nil diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 7f0685e306..0874538320 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -216,6 +216,22 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { return t.preimages.preimage(common.BytesToHash(shaKey)) } +func (t *StateTrie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) { + // Write all the pre-images to the actual disk database + if len(t.getSecKeyCache()) > 0 { + if t.preimages != nil { + preimages := make(map[common.Hash][]byte) + for hk, key := range t.secKeyCache { + preimages[common.BytesToHash([]byte(hk))] = key + } + t.preimages.insertPreimage(preimages) + } + t.secKeyCache = make(map[string][]byte) + } + // Commit the trie and return its modified nodeset. + return t.trie.CommitAndObtainAccessList(collectLeaf) +} + // Commit collects all dirty nodes in the trie and replaces them with the // corresponding node hash. All collected nodes (including dirty leaves if // collectLeaf is true) will be encapsulated into a nodeset for return. diff --git a/trie/trie.go b/trie/trie.go index 07467ac69c..ab3c706a60 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -601,6 +601,18 @@ func (t *Trie) Hash() common.Hash { return common.BytesToHash(hash.(hashNode)) } +func (t *Trie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) { + accessList := t.tracer.accessList + // Commit will reset the tracer accessList, so after this + // operation, we have full ownership of the map (hence: no need to + // deep-copy or even copy). + rootHash, nodes, err := t.Commit(collectLeaf) + if err != nil { + return rootHash, nodes, nil, err + } + return rootHash, nodes, accessList, err +} + // Commit collects all dirty nodes in the trie and replaces them with the // corresponding node hash. All collected nodes (including dirty leaves if // collectLeaf is true) will be encapsulated into a nodeset for return. @@ -608,8 +620,8 @@ func (t *Trie) Hash() common.Hash { // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { - defer t.tracer.reset() defer func() { + t.tracer.reset() t.committed = true }() // Trie is empty and can be classified into two types of situations: diff --git a/trie/verkle.go b/trie/verkle.go index 89e2e53408..b8fbc0c81e 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -218,6 +218,10 @@ func (t *VerkleTrie) Hash() common.Hash { return t.root.Commit().Bytes() } +func (t *VerkleTrie) CommitAndObtainAccessList(collectLeaf bool) (common.Hash, *trienode.NodeSet, map[string][]byte, error) { + panic("not implemented") +} + // Commit writes all nodes to the tree's memory database. func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) { root, ok := t.root.(*verkle.InternalNode)