From 2d8c563e169275fb52471c1b0fcdd676457abc66 Mon Sep 17 00:00:00 2001 From: philip-morlier Date: Thu, 6 Apr 2023 18:10:25 -0700 Subject: [PATCH 1/5] intial work to bring through state trie --- core/trie.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 core/trie.go diff --git a/core/trie.go b/core/trie.go new file mode 100644 index 0000000..406dbaf --- /dev/null +++ b/core/trie.go @@ -0,0 +1,154 @@ +package core + +import ( + "math/big" +) + +// Trie is a Ethereum Merkle Patricia trie. +type Trie interface { + // GetKey returns the sha3 preimage of a hashed key that was previously used + // to store a value. + // + // TODO(fjl): remove this when StateTrie is removed + GetKey([]byte) []byte + + // TryGet returns the value for key stored in the trie. The value bytes must + // not be modified by the caller. If a node was not found in the database, a + // trie.MissingNodeError is returned. + TryGet(key []byte) ([]byte, error) + + // TryGetAccount abstracts an account read from the trie. It retrieves the + // account blob from the trie with provided account address and decodes it + // with associated decoding algorithm. If the specified account is not in + // the trie, nil will be returned. If the trie is corrupted(e.g. some nodes + // are missing or the account blob is incorrect for decoding), an error will + // be returned. + TryGetAccount(address Address) (*StateAccount, error) + + + // TryUpdate 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 + // database, a trie.MissingNodeError is returned. + // TryUpdate(key, value []byte) error + + // TryUpdateAccount abstracts an account write to the trie. It encodes the + // provided account object with associated algorithm and then updates it + // in the trie with provided address. + // TryUpdateAccount(address Address, account *StateAccount) error + + // 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 + + // TryDeleteAccount abstracts an account deletion from the trie. + // TryDeleteAccount(address Address) error + + // Hash returns the root hash of the trie. It does not write to the database and + // can be used even if the trie doesn't have one. + Hash() Hash + + // Commit collects all dirty nodes in the trie and replace them with the + // corresponding node hash. All collected nodes(including dirty leaves if + // collectLeaf is true) will be encapsulated into a nodeset for return. + // The returned nodeset can be nil if the trie is clean(nothing to commit). + // 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 + // Commit(collectLeaf bool) (Hash, *NodeSet) + + // NodeIterator returns an iterator that returns nodes of the trie. Iteration + // starts at the key after the given start key. + NodeIterator(startKey []byte) NodeIterator + + // Prove constructs a Merkle proof for key. The result contains all encoded nodes + // on the path to the value at key. The value itself is also included in the last + // node and can be retrieved by verifying the proof. + // + // If the trie does not contain a value for key, the returned proof contains all + // nodes of the longest existing prefix of the key (at least the root), ending + // with the node that proves the absence of the key. + Prove(key []byte, fromLevel uint, proofDb KeyValueWriter) error +} + +// StateAccount is the Ethereum consensus representation of accounts. +// These objects are stored in the main account trie. +type StateAccount struct { + Nonce uint64 + Balance *big.Int + Root Hash // merkle root of the storage trie + CodeHash []byte +} + +// NodeIterator is an iterator to traverse the trie pre-order. +type NodeIterator interface { + // Next moves the iterator to the next node. If the parameter is false, any child + // nodes will be skipped. + Next(bool) bool + + // Error returns the error status of the iterator. + Error() error + + // Hash returns the hash of the current node. + Hash() Hash + + // Parent returns the hash of the parent of the current node. The hash may be the one + // grandparent if the immediate parent is an internal node with no hash. + Parent() Hash + + // Path returns the hex-encoded path to the current node. + // Callers must not retain references to the return value after calling Next. + // For leaf nodes, the last element of the path is the 'terminator symbol' 0x10. + Path() []byte + + // NodeBlob returns the rlp-encoded value of the current iterated node. + // If the node is an embedded node in its parent, nil is returned then. + NodeBlob() []byte + + // Leaf returns true iff the current node is a leaf node. + Leaf() bool + + // LeafKey returns the key of the leaf. The method panics if the iterator is not + // positioned at a leaf. Callers must not retain references to the value after + // calling Next. + LeafKey() []byte + + // LeafBlob returns the content of the leaf. The method panics if the iterator + // is not positioned at a leaf. Callers must not retain references to the value + // after calling Next. + LeafBlob() []byte + + // LeafProof returns the Merkle proof of the leaf. The method panics if the + // iterator is not positioned at a leaf. Callers must not retain references + // to the value after calling Next. + LeafProof() [][]byte + + // AddResolver sets a node resolver to use for looking up trie nodes before + // reaching into the real persistent layer. + // + // This is not required for normal operation, rather is an optimization for + // cases where trie nodes can be recovered from some external mechanism without + // reading from disk. In those cases, this resolver allows short circuiting + // accesses and returning them from memory. + // + // Before adding a similar mechanism to any other place in Geth, consider + // making trie.Database an interface and wrapping at that level. It's a huge + // refactor, but it could be worth it if another occurrence arises. + AddResolver(NodeResolver) +} + +// NodeResolver is used for looking up trie nodes before reaching into the real +// persistent layer. This is not mandatory, rather is an optimization for cases +// where trie nodes can be recovered from some external mechanism without reading +// from disk. In those cases, this resolver allows short circuiting accesses and +// returning them from memory. +type NodeResolver func(owner Hash, path []byte, hash Hash) []byte + + +// KeyValueWriter wraps the Put method of a backing data store. +type KeyValueWriter interface { + // Put inserts the given value into the key-value data store. + Put(key []byte, value []byte) error + + // Delete removes the key from the key-value data store. + Delete(key []byte) error +} \ No newline at end of file From 40f38568e000c44a76f8b98f840cd9e046220805 Mon Sep 17 00:00:00 2001 From: philip-morlier Date: Fri, 7 Apr 2023 10:48:42 -0700 Subject: [PATCH 2/5] Exposed Geth core/state/Trie Added three interfaces which enable plugins to access the state trie appended to the backend object. --- core/interface.go | 39 +++++++++++- core/trie.go | 154 ---------------------------------------------- 2 files changed, 37 insertions(+), 156 deletions(-) delete mode 100644 core/trie.go diff --git a/core/interface.go b/core/interface.go index 48eb7e9..073fb48 100644 --- a/core/interface.go +++ b/core/interface.go @@ -219,8 +219,43 @@ type BlockContext struct { type Context interface { Set(string, string) error - String(string) string - Bool(string) bool } + +type Trie interface { + GetKey([]byte) []byte + TryGet(key []byte) ([]byte, error) + TryGetAccount(address Address) (*StateAccount, error) + Hash() Hash + NodeIterator(startKey []byte) NodeIterator + Prove(key []byte, fromLevel uint, proofDb KeyValueWriter) error +} + +type StateAccount struct { + Nonce uint64 + Balance *big.Int + Root Hash // merkle root of the storage trie + CodeHash []byte +} + +type NodeIterator interface { + Next(bool) bool + Error() error + Hash() Hash + Parent() Hash + Path() []byte + NodeBlob() []byte + Leaf() bool + LeafKey() []byte + LeafBlob() []byte + LeafProof() [][]byte + AddResolver(NodeResolver) +} + +type NodeResolver func(owner Hash, path []byte, hash Hash) []byte + +type KeyValueWriter interface { + Put(key []byte, value []byte) error + Delete(key []byte) error +} \ No newline at end of file diff --git a/core/trie.go b/core/trie.go deleted file mode 100644 index 406dbaf..0000000 --- a/core/trie.go +++ /dev/null @@ -1,154 +0,0 @@ -package core - -import ( - "math/big" -) - -// Trie is a Ethereum Merkle Patricia trie. -type Trie interface { - // GetKey returns the sha3 preimage of a hashed key that was previously used - // to store a value. - // - // TODO(fjl): remove this when StateTrie is removed - GetKey([]byte) []byte - - // TryGet returns the value for key stored in the trie. The value bytes must - // not be modified by the caller. If a node was not found in the database, a - // trie.MissingNodeError is returned. - TryGet(key []byte) ([]byte, error) - - // TryGetAccount abstracts an account read from the trie. It retrieves the - // account blob from the trie with provided account address and decodes it - // with associated decoding algorithm. If the specified account is not in - // the trie, nil will be returned. If the trie is corrupted(e.g. some nodes - // are missing or the account blob is incorrect for decoding), an error will - // be returned. - TryGetAccount(address Address) (*StateAccount, error) - - - // TryUpdate 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 - // database, a trie.MissingNodeError is returned. - // TryUpdate(key, value []byte) error - - // TryUpdateAccount abstracts an account write to the trie. It encodes the - // provided account object with associated algorithm and then updates it - // in the trie with provided address. - // TryUpdateAccount(address Address, account *StateAccount) error - - // 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 - - // TryDeleteAccount abstracts an account deletion from the trie. - // TryDeleteAccount(address Address) error - - // Hash returns the root hash of the trie. It does not write to the database and - // can be used even if the trie doesn't have one. - Hash() Hash - - // Commit collects all dirty nodes in the trie and replace them with the - // corresponding node hash. All collected nodes(including dirty leaves if - // collectLeaf is true) will be encapsulated into a nodeset for return. - // The returned nodeset can be nil if the trie is clean(nothing to commit). - // 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 - // Commit(collectLeaf bool) (Hash, *NodeSet) - - // NodeIterator returns an iterator that returns nodes of the trie. Iteration - // starts at the key after the given start key. - NodeIterator(startKey []byte) NodeIterator - - // Prove constructs a Merkle proof for key. The result contains all encoded nodes - // on the path to the value at key. The value itself is also included in the last - // node and can be retrieved by verifying the proof. - // - // If the trie does not contain a value for key, the returned proof contains all - // nodes of the longest existing prefix of the key (at least the root), ending - // with the node that proves the absence of the key. - Prove(key []byte, fromLevel uint, proofDb KeyValueWriter) error -} - -// StateAccount is the Ethereum consensus representation of accounts. -// These objects are stored in the main account trie. -type StateAccount struct { - Nonce uint64 - Balance *big.Int - Root Hash // merkle root of the storage trie - CodeHash []byte -} - -// NodeIterator is an iterator to traverse the trie pre-order. -type NodeIterator interface { - // Next moves the iterator to the next node. If the parameter is false, any child - // nodes will be skipped. - Next(bool) bool - - // Error returns the error status of the iterator. - Error() error - - // Hash returns the hash of the current node. - Hash() Hash - - // Parent returns the hash of the parent of the current node. The hash may be the one - // grandparent if the immediate parent is an internal node with no hash. - Parent() Hash - - // Path returns the hex-encoded path to the current node. - // Callers must not retain references to the return value after calling Next. - // For leaf nodes, the last element of the path is the 'terminator symbol' 0x10. - Path() []byte - - // NodeBlob returns the rlp-encoded value of the current iterated node. - // If the node is an embedded node in its parent, nil is returned then. - NodeBlob() []byte - - // Leaf returns true iff the current node is a leaf node. - Leaf() bool - - // LeafKey returns the key of the leaf. The method panics if the iterator is not - // positioned at a leaf. Callers must not retain references to the value after - // calling Next. - LeafKey() []byte - - // LeafBlob returns the content of the leaf. The method panics if the iterator - // is not positioned at a leaf. Callers must not retain references to the value - // after calling Next. - LeafBlob() []byte - - // LeafProof returns the Merkle proof of the leaf. The method panics if the - // iterator is not positioned at a leaf. Callers must not retain references - // to the value after calling Next. - LeafProof() [][]byte - - // AddResolver sets a node resolver to use for looking up trie nodes before - // reaching into the real persistent layer. - // - // This is not required for normal operation, rather is an optimization for - // cases where trie nodes can be recovered from some external mechanism without - // reading from disk. In those cases, this resolver allows short circuiting - // accesses and returning them from memory. - // - // Before adding a similar mechanism to any other place in Geth, consider - // making trie.Database an interface and wrapping at that level. It's a huge - // refactor, but it could be worth it if another occurrence arises. - AddResolver(NodeResolver) -} - -// NodeResolver is used for looking up trie nodes before reaching into the real -// persistent layer. This is not mandatory, rather is an optimization for cases -// where trie nodes can be recovered from some external mechanism without reading -// from disk. In those cases, this resolver allows short circuiting accesses and -// returning them from memory. -type NodeResolver func(owner Hash, path []byte, hash Hash) []byte - - -// KeyValueWriter wraps the Put method of a backing data store. -type KeyValueWriter interface { - // Put inserts the given value into the key-value data store. - Put(key []byte, value []byte) error - - // Delete removes the key from the key-value data store. - Delete(key []byte) error -} \ No newline at end of file From 6b776ae75c5f61beeb00616cebc09453b04ef5a4 Mon Sep 17 00:00:00 2001 From: philip-morlier Date: Fri, 7 Apr 2023 14:28:23 -0700 Subject: [PATCH 3/5] Added GetTrie and GetAccountTrie to core/interace.go --- core/interface.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/interface.go b/core/interface.go index 073fb48..b8b2c80 100644 --- a/core/interface.go +++ b/core/interface.go @@ -55,6 +55,9 @@ type Backend interface { SubscribePendingLogsEvent(ch chan<- [][]byte) Subscription // RLP Encoded logs SubscribeRemovedLogsEvent(ch chan<- []byte) Subscription // RLP encoded logs + GetTrie(hash Hash) (Trie, error) + GetAccountTrie(stateRoot Hash, account Address) (Trie, error) + // ChainConfig() *params.ChainConfig // Engine() consensus.Engine } From 714e2aa4e47c21d7352fc8ccdd6a71fc1ffb3acf Mon Sep 17 00:00:00 2001 From: philip-morlier Date: Thu, 27 Apr 2023 08:25:11 -0700 Subject: [PATCH 4/5] Tag augmented to mark merge of geth v1.11.6 Atypically, with geth v1.11.6, we needed to update dependencies in cardinal and plugeth-plugins. This commit and tag is meant to serve as marker for when those changes were merged across the cardianl eco-system. From 53047f4c7173b0c75629dbfc0f501adcb538c5c0 Mon Sep 17 00:00:00 2001 From: philip-morlier Date: Fri, 5 May 2023 14:04:28 -0700 Subject: [PATCH 5/5] Changed Trie methods to conform to geth v1.11.6 --- core/interface.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/interface.go b/core/interface.go index b8b2c80..318f312 100644 --- a/core/interface.go +++ b/core/interface.go @@ -228,8 +228,7 @@ type Context interface { type Trie interface { GetKey([]byte) []byte - TryGet(key []byte) ([]byte, error) - TryGetAccount(address Address) (*StateAccount, error) + GetAccount(address Address) (*StateAccount, error) Hash() Hash NodeIterator(startKey []byte) NodeIterator Prove(key []byte, fromLevel uint, proofDb KeyValueWriter) error @@ -261,4 +260,4 @@ type NodeResolver func(owner Hash, path []byte, hash Hash) []byte type KeyValueWriter interface { Put(key []byte, value []byte) error Delete(key []byte) error -} \ No newline at end of file +}