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 (
"github.com/ipfs/go-cid"

View File

@ -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

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}