code referenced by codehash in contract accounts needs to be present in kv dstore for the trie to be considered complete by the state node iterator

This commit is contained in:
Ian Norden 2020-07-30 19:23:09 -05:00
parent d26bb68995
commit 00223269f8
2 changed files with 29 additions and 28 deletions

View File

@ -17,8 +17,6 @@
package validator package validator
import ( import (
"fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -75,20 +73,11 @@ func NewValidator(kvs ethdb.KeyValueStore, database ethdb.Database) *Validator {
// This does consider child storage tries // This does consider child storage tries
func (v *Validator) ValidateTrie(stateRoot common.Hash) error { func (v *Validator) ValidateTrie(stateRoot common.Hash) error {
// Generate the state.NodeIterator for this root // Generate the state.NodeIterator for this root
stateDB, err := state.New(common.Hash{}, v.stateDatabase, nil) stateDB, err := state.New(stateRoot, v.stateDatabase, nil)
if err != nil { if err != nil {
return err return err
} }
it := state.NewNodeIterator(stateDB) it := state.NewNodeIterator(stateDB)
// state.NodeIterator won't throw an error if we can't find the root node
// check if it exists first
exists, err := v.kvs.Has(stateRoot.Bytes())
if err != nil {
return err
}
if !exists {
return fmt.Errorf("root node for hash %s does not exist in database", stateRoot.Hex())
}
for it.Next() { for it.Next() {
// iterate through entire state trie and descendent storage tries // iterate through entire state trie and descendent storage tries
// it.Next() will return false when we have either completed iteration of the entire trie or have ran into an error (e.g. a missing node) // it.Next() will return false when we have either completed iteration of the entire trie or have ran into an error (e.g. a missing node)

View File

@ -17,6 +17,7 @@
package validator_test package validator_test
import ( import (
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -62,10 +63,12 @@ var (
}) })
stateRoot = crypto.Keccak256Hash(stateBranchRootNode) stateRoot = crypto.Keccak256Hash(stateBranchRootNode)
mockCode = []byte{1, 2, 3, 4, 5}
codeHash = crypto.Keccak256Hash(mockCode)
contractAccount, _ = rlp.EncodeToBytes(state.Account{ contractAccount, _ = rlp.EncodeToBytes(state.Account{
Nonce: 1, Nonce: 1,
Balance: big.NewInt(0), Balance: big.NewInt(0),
CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(), CodeHash: codeHash.Bytes(),
Root: crypto.Keccak256Hash(storageBranchRootNode), Root: crypto.Keccak256Hash(storageBranchRootNode),
}) })
contractAccountLeafNode, _ = rlp.EncodeToBytes([]interface{}{ contractAccountLeafNode, _ = rlp.EncodeToBytes([]interface{}{
@ -204,31 +207,40 @@ var _ = Describe("PG-IPFS Validator", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })
It("Returns an error the state root node is missing", func() { It("Returns an error the state root node is missing", func() {
loadTrie(missingRootStateNodes, trieStorageNodes) // we write code to ethdb, there should probably be an EthCode IPLD codec
// but there isn't, and we don't need one here since blockstore keys are mh-derived
loadTrie(append(missingRootStateNodes, mockCode), trieStorageNodes)
err = v.ValidateTrie(stateRoot) err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("does not exist in database")) Expect(err.Error()).To(ContainSubstring("missing trie node"))
}) })
It("Fails to return an error if the storage root node is missing", func() { It("Returns an error if the storage root node is missing", func() {
// NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator loadTrie(append(trieStateNodes, mockCode), missingRootStorageNodes)
loadTrie(trieStateNodes, missingRootStorageNodes)
err = v.ValidateTrie(stateRoot) err = v.ValidateTrie(stateRoot)
Expect(err).ToNot(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
}) })
It("Fails to return an error if the entire state (state trie and storage tries) cannot be validated", func() { It("Returns an error if the state trie is missing node(s)", func() {
// NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator loadTrie(append(missingNodeStateNodes, mockCode), trieStorageNodes)
loadTrie(missingNodeStateNodes, trieStorageNodes)
err = v.ValidateTrie(stateRoot) err = v.ValidateTrie(stateRoot)
Expect(err).ToNot(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
}) })
It("Fails to return an error if the entire state (state trie and storage tries) cannot be validated", func() { It("Returns an error if the storage trie is missing node(s)", func() {
// NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator loadTrie(append(trieStateNodes, mockCode), missingNodeStorageNodes)
loadTrie(trieStateNodes, missingNodeStorageNodes)
err = v.ValidateTrie(stateRoot) err = v.ValidateTrie(stateRoot)
Expect(err).ToNot(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("missing trie node"))
})
It("Returns an error if contract code is missing", func() {
loadTrie(trieStateNodes, trieStorageNodes)
err = v.ValidateTrie(stateRoot)
Expect(err).To(HaveOccurred())
subStr := fmt.Sprintf("code %s: sql: no rows in result set", codeHash.Hex()[2:])
Expect(err.Error()).To(ContainSubstring(subStr))
}) })
It("Returns no error if the entire state (state trie and storage tries) can be validated", func() { It("Returns no error if the entire state (state trie and storage tries) can be validated", func() {
loadTrie(trieStateNodes, trieStorageNodes) loadTrie(append(trieStateNodes, mockCode), trieStorageNodes)
err = v.ValidateTrie(stateRoot) err = v.ValidateTrie(stateRoot)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
}) })