From 00223269f867b68d97d622f7f89385f9bd979960 Mon Sep 17 00:00:00 2001 From: Ian Norden Date: Thu, 30 Jul 2020 19:23:09 -0500 Subject: [PATCH] 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 --- pkg/validator.go | 13 +------------ pkg/validator_test.go | 44 +++++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/pkg/validator.go b/pkg/validator.go index 64b5c69..144b263 100644 --- a/pkg/validator.go +++ b/pkg/validator.go @@ -17,8 +17,6 @@ package validator import ( - "fmt" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "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 func (v *Validator) ValidateTrie(stateRoot common.Hash) error { // 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 { return err } 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() { // 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) diff --git a/pkg/validator_test.go b/pkg/validator_test.go index 2bf453d..eec4505 100644 --- a/pkg/validator_test.go +++ b/pkg/validator_test.go @@ -17,6 +17,7 @@ package validator_test import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -62,10 +63,12 @@ var ( }) stateRoot = crypto.Keccak256Hash(stateBranchRootNode) + mockCode = []byte{1, 2, 3, 4, 5} + codeHash = crypto.Keccak256Hash(mockCode) contractAccount, _ = rlp.EncodeToBytes(state.Account{ Nonce: 1, Balance: big.NewInt(0), - CodeHash: common.HexToHash("0xaaea5efba4fd7b45d7ec03918ac5d8b31aa93b48986af0e6b591f0f087c80127").Bytes(), + CodeHash: codeHash.Bytes(), Root: crypto.Keccak256Hash(storageBranchRootNode), }) contractAccountLeafNode, _ = rlp.EncodeToBytes([]interface{}{ @@ -204,31 +207,40 @@ var _ = Describe("PG-IPFS Validator", func() { Expect(err).ToNot(HaveOccurred()) }) 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) 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() { - // NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator - loadTrie(trieStateNodes, missingRootStorageNodes) + It("Returns an error if the storage root node is missing", func() { + loadTrie(append(trieStateNodes, mockCode), missingRootStorageNodes) 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() { - // NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator - loadTrie(missingNodeStateNodes, trieStorageNodes) + It("Returns an error if the state trie is missing node(s)", func() { + loadTrie(append(missingNodeStateNodes, mockCode), trieStorageNodes) 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() { - // NOTE this failure was not expected and renders this approach unreliable, this is an issue with the go-ethereum core/state/iterator.NodeIterator - loadTrie(trieStateNodes, missingNodeStorageNodes) + It("Returns an error if the storage trie is missing node(s)", func() { + loadTrie(append(trieStateNodes, mockCode), missingNodeStorageNodes) 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() { - loadTrie(trieStateNodes, trieStorageNodes) + loadTrie(append(trieStateNodes, mockCode), trieStorageNodes) err = v.ValidateTrie(stateRoot) Expect(err).ToNot(HaveOccurred()) })