Fix node resolution; use Cid struct as key

was erroneously storing the cid in the fullNode's flag.cache
This commit is contained in:
Roy Crihfield 2023-04-20 23:53:37 +08:00
parent 3f0e36c0a3
commit 88e7a394d0
5 changed files with 45 additions and 53 deletions

View File

@ -1,4 +1,4 @@
package util package internal
import ( import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"

View File

@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/statediff/indexer/ipld" "github.com/ethereum/go-ethereum/statediff/indexer/ipld"
lru "github.com/hashicorp/golang-lru" 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" "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 { if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
return code, nil return code, nil
} }
// TODO - use non panicking codeCID, err := internal.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes())
codeCID := ipld.Keccak256ToCid(ipld.RawBinary, codeHash.Bytes()) if err != nil {
// if err != nil { return nil, err
// return nil, err }
// }
code, err := db.db.DiskDB().Get(codeCID.Bytes()) code, err := db.db.DiskDB().Get(codeCID.Bytes())
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -22,12 +22,13 @@ import (
"github.com/VictoriaMetrics/fastcache" "github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ipfs/go-cid"
) )
type CidBytes = []byte type CidKey = cid.Cid
func isEmpty(key CidBytes) bool { func isEmpty(key CidKey) bool {
return len(key) == 0 return len(key.KeyString()) == 0
} }
// Database is an intermediate read-only layer between the trie data structures and // 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 return db.diskdb
} }
// node retrieves a cached trie node from memory, or returns nil if none can be // Node retrieves an encoded trie node by CID. If it cannot be found
// found in the memory cache. // cached in memory, it queries the persistent database.
func (db *Database) node(key CidBytes) (node, error) { func (db *Database) Node(key CidKey) ([]byte, 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) {
// It doesn't make sense to retrieve the metaroot // It doesn't make sense to retrieve the metaroot
if isEmpty(key) { if isEmpty(key) {
return nil, errors.New("not found") return nil, errors.New("not found")
} }
cidbytes := key.Bytes()
// Retrieve the node from the clean cache if available // Retrieve the node from the clean cache if available
if db.cleans != nil { 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 return enc, nil
} }
} }
// Content unavailable in memory, attempt to retrieve from disk // Content unavailable in memory, attempt to retrieve from disk
enc, err := db.diskdb.Get(key) enc, err := db.diskdb.Get(cidbytes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(enc) != 0 { if len(enc) != 0 {
if db.cleans != nil { if db.cleans != nil {
db.cleans.Set(key[:], enc) db.cleans.Set(cidbytes, enc)
} }
return enc, nil return enc, nil
} }

View File

@ -28,7 +28,7 @@ import (
// to retrieve the meta root. // to retrieve the meta root.
func TestDatabaseMetarootFetch(t *testing.T) { func TestDatabaseMetarootFetch(t *testing.T) {
db := trie.NewDatabase(memorydb.New()) 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") t.Fatalf("metaroot retrieval succeeded")
} }
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
util "github.com/cerc-io/ipld-eth-statedb/internal"
"github.com/ethereum/go-ethereum/statediff/indexer/ipld" "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 { if hash == nil {
return nil, origNode, 0, errors.New("non-consensus node") 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 return blob, origNode, 1, err
} }
// Path still needs to be traversed, descend into children // 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 // resolveHash loads node from the underlying database with the provided
// node hash and path prefix. // node hash and path prefix.
func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
cid := ipld.Keccak256ToCid(t.codec, n) cid, err := util.Keccak256ToCid(t.codec, n)
node, err := t.db.node(cid.Bytes()) 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 { if err != nil {
return nil, err 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 // resolveHash loads rlp-encoded node blob from the underlying database
// with the provided node hash and path prefix. // with the provided node hash and path prefix.
func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) { func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) {
cid := ipld.Keccak256ToCid(t.codec, n) cid, err := util.Keccak256ToCid(t.codec, n)
blob, _ := t.db.Node(cid.Bytes()) if err != nil {
return nil, err
}
blob, err := t.db.Node(cid)
if err != nil {
return nil, err
}
if len(blob) != 0 { if len(blob) != 0 {
return blob, nil 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 // 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. // database and can be used even if the trie doesn't have one.
func (t *Trie) Hash() common.Hash { func (t *Trie) Hash() common.Hash {
hash, cached, _ := t.hashRoot() hash, cached := t.hashRoot()
t.root = cached t.root = cached
return common.BytesToHash(hash.(hashNode)) return common.BytesToHash(hash.(hashNode))
} }
// hashRoot calculates the root hash of the given trie // 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 { 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 // If the number of changes is below 100, we let one thread handle it
h := newHasher(t.unhashed >= 100) h := newHasher(t.unhashed >= 100)
defer returnHasherToPool(h) defer returnHasherToPool(h)
hashed, cached := h.hash(t.root, true) hashed, cached := h.hash(t.root, true)
t.unhashed = 0 t.unhashed = 0
return hashed, cached, nil return hashed, cached
} }