From 88e7a394d0d2af3399a814fe1eed475555794deb Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 20 Apr 2023 23:53:37 +0800 Subject: [PATCH] Fix node resolution; use Cid struct as key was erroneously storing the cid in the fullNode's flag.cache --- internal/util.go | 2 +- trie_by_cid/state/database.go | 10 +++---- trie_by_cid/trie/database.go | 48 +++++++------------------------ trie_by_cid/trie/database_test.go | 2 +- trie_by_cid/trie/trie.go | 36 +++++++++++++++++------ 5 files changed, 45 insertions(+), 53 deletions(-) diff --git a/internal/util.go b/internal/util.go index 6c25c0f..a19df0a 100644 --- a/internal/util.go +++ b/internal/util.go @@ -1,4 +1,4 @@ -package util +package internal import ( "github.com/ipfs/go-cid" diff --git a/trie_by_cid/state/database.go b/trie_by_cid/state/database.go index 07fd020..30adbb7 100644 --- a/trie_by_cid/state/database.go +++ b/trie_by_cid/state/database.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/statediff/indexer/ipld" lru "github.com/hashicorp/golang-lru" + "github.com/cerc-io/ipld-eth-statedb/internal" "github.com/cerc-io/ipld-eth-statedb/trie_by_cid/trie" ) @@ -97,11 +98,10 @@ func (db *cachingDB) ContractCode(codeHash common.Hash) ([]byte, error) { if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 { return code, nil } - // TODO - use non panicking - codeCID := ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()) - // if err != nil { - // return nil, err - // } + codeCID, err := internal.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()) + if err != nil { + return nil, err + } code, err := db.db.DiskDB().Get(codeCID.Bytes()) if err != nil { return nil, err diff --git a/trie_by_cid/trie/database.go b/trie_by_cid/trie/database.go index 82bad23..d13cb49 100644 --- a/trie_by_cid/trie/database.go +++ b/trie_by_cid/trie/database.go @@ -22,12 +22,13 @@ import ( "github.com/VictoriaMetrics/fastcache" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" + "github.com/ipfs/go-cid" ) -type CidBytes = []byte +type CidKey = cid.Cid -func isEmpty(key CidBytes) bool { - return len(key) == 0 +func isEmpty(key CidKey) bool { + return len(key.KeyString()) == 0 } // Database is an intermediate read-only layer between the trie data structures and @@ -73,57 +74,30 @@ func (db *Database) DiskDB() ethdb.KeyValueStore { return db.diskdb } -// node retrieves a cached trie node from memory, or returns nil if none can be -// found in the memory cache. -func (db *Database) node(key CidBytes) (node, error) { - // Retrieve the node from the clean cache if available - if db.cleans != nil { - if enc := db.cleans.Get(nil, key); enc != nil { - // The returned value from cache is in its own copy, - // safe to use mustDecodeNodeUnsafe for decoding. - return decodeNodeUnsafe(key, enc) - } - } - - // Content unavailable in memory, attempt to retrieve from disk - enc, err := db.diskdb.Get(key) - if err != nil { - return nil, err - } - if enc == nil { - return nil, nil - } - if db.cleans != nil { - db.cleans.Set(key, enc) - } - // The returned value from database is in its own copy, - // safe to use mustDecodeNodeUnsafe for decoding. - return decodeNodeUnsafe(key, enc) -} - -// Node retrieves an encoded cached trie node from memory. If it cannot be found -// cached, the method queries the persistent database for the content. -func (db *Database) Node(key CidBytes) ([]byte, error) { +// Node retrieves an encoded trie node by CID. If it cannot be found +// cached in memory, it queries the persistent database. +func (db *Database) Node(key CidKey) ([]byte, error) { // It doesn't make sense to retrieve the metaroot if isEmpty(key) { return nil, errors.New("not found") } + cidbytes := key.Bytes() // Retrieve the node from the clean cache if available if db.cleans != nil { - if enc := db.cleans.Get(nil, key); enc != nil { + if enc := db.cleans.Get(nil, cidbytes); enc != nil { return enc, nil } } // Content unavailable in memory, attempt to retrieve from disk - enc, err := db.diskdb.Get(key) + enc, err := db.diskdb.Get(cidbytes) if err != nil { return nil, err } if len(enc) != 0 { if db.cleans != nil { - db.cleans.Set(key[:], enc) + db.cleans.Set(cidbytes, enc) } return enc, nil } diff --git a/trie_by_cid/trie/database_test.go b/trie_by_cid/trie/database_test.go index 599cc58..a800135 100644 --- a/trie_by_cid/trie/database_test.go +++ b/trie_by_cid/trie/database_test.go @@ -28,7 +28,7 @@ import ( // to retrieve the meta root. func TestDatabaseMetarootFetch(t *testing.T) { db := trie.NewDatabase(memorydb.New()) - if _, err := db.Node(trie.CidBytes(nil)); err == nil { + if _, err := db.Node(trie.CidKey{}); err == nil { t.Fatalf("metaroot retrieval succeeded") } } diff --git a/trie_by_cid/trie/trie.go b/trie_by_cid/trie/trie.go index f82c128..78d917a 100644 --- a/trie_by_cid/trie/trie.go +++ b/trie_by_cid/trie/trie.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + util "github.com/cerc-io/ipld-eth-statedb/internal" "github.com/ethereum/go-ethereum/statediff/indexer/ipld" ) @@ -161,7 +162,11 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new if hash == nil { return nil, origNode, 0, errors.New("non-consensus node") } - blob, err := t.db.Node(hash) + cid, err := util.Keccak256ToCid(t.codec, hash) + if err != nil { + return nil, origNode, 0, err + } + blob, err := t.db.Node(cid) return blob, origNode, 1, err } // Path still needs to be traversed, descend into children @@ -206,8 +211,15 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new // resolveHash loads node from the underlying database with the provided // node hash and path prefix. func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { - cid := ipld.Keccak256ToCid(t.codec, n) - node, err := t.db.node(cid.Bytes()) + cid, err := util.Keccak256ToCid(t.codec, n) + if err != nil { + return nil, err + } + enc, err := t.db.Node(cid) + if err != nil { + return nil, err + } + node, err := decodeNodeUnsafe(n, enc) if err != nil { return nil, err } @@ -220,8 +232,14 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { // resolveHash loads rlp-encoded node blob from the underlying database // with the provided node hash and path prefix. func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) { - cid := ipld.Keccak256ToCid(t.codec, n) - blob, _ := t.db.Node(cid.Bytes()) + cid, err := util.Keccak256ToCid(t.codec, n) + if err != nil { + return nil, err + } + blob, err := t.db.Node(cid) + if err != nil { + return nil, err + } if len(blob) != 0 { return blob, nil } @@ -231,20 +249,20 @@ func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, 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. func (t *Trie) Hash() common.Hash { - hash, cached, _ := t.hashRoot() + hash, cached := t.hashRoot() t.root = cached return common.BytesToHash(hash.(hashNode)) } // hashRoot calculates the root hash of the given trie -func (t *Trie) hashRoot() (node, node, error) { +func (t *Trie) hashRoot() (node, node) { if t.root == nil { - return hashNode(emptyRoot.Bytes()), nil, nil + return hashNode(emptyRoot.Bytes()), nil } // If the number of changes is below 100, we let one thread handle it h := newHasher(t.unhashed >= 100) defer returnHasherToPool(h) hashed, cached := h.hash(t.root, true) t.unhashed = 0 - return hashed, cached, nil + return hashed, cached }