diff --git a/common/debug/experiments.go b/common/debug/experiments.go index 2abd9db87..d9ce85588 100644 --- a/common/debug/experiments.go +++ b/common/debug/experiments.go @@ -4,46 +4,9 @@ import ( "os" "strconv" "sync" - "sync/atomic" "time" ) -// atomic: bit 0 is the value, bit 1 is the initialized flag -var getNodeData uint32 - -const ( - gndValueFlag = 1 << iota - gndInitializedFlag -) - -// IsGetNodeData indicates whether the GetNodeData functionality should be enabled. -// By default that's driven by the presence or absence of DISABLE_GET_NODE_DATA environment variable. -func IsGetNodeData() bool { - x := atomic.LoadUint32(&getNodeData) - if x&gndInitializedFlag != 0 { // already initialized - return x&gndValueFlag != 0 - } - - RestoreGetNodeData() - return IsGetNodeData() -} - -// RestoreGetNodeData enables or disables the GetNodeData functionality -// according to the presence or absence of GET_NODE_DATA environment variable. -func RestoreGetNodeData() { - _, envVarSet := os.LookupEnv("GET_NODE_DATA") - OverrideGetNodeData(envVarSet) -} - -// OverrideGetNodeData allows to explicitly enable or disable the GetNodeData functionality. -func OverrideGetNodeData(val bool) { - if val { - atomic.StoreUint32(&getNodeData, gndInitializedFlag|gndValueFlag) - } else { - atomic.StoreUint32(&getNodeData, gndInitializedFlag) - } -} - var ( bigRoTx uint getBigRoTx sync.Once diff --git a/turbo/trie/trie.go b/turbo/trie/trie.go index afadc7bf6..33e067e87 100644 --- a/turbo/trie/trie.go +++ b/turbo/trie/trie.go @@ -25,11 +25,9 @@ import ( libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/cmp" "github.com/ledgerwatch/erigon/common" - "github.com/ledgerwatch/erigon/common/debug" "github.com/ledgerwatch/erigon/core/types/accounts" "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/ethdb" - "github.com/ledgerwatch/log/v3" ) var ( @@ -52,8 +50,6 @@ type Trie struct { root node newHasherFunc func() *hasher - - hashMap map[common.Hash]node } // New creates a trie with an existing root node from db. @@ -67,7 +63,6 @@ type Trie struct { func New(root common.Hash) *Trie { trie := &Trie{ newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ false) }, - hashMap: make(map[common.Hash]node), } if (root != common.Hash{}) && root != EmptyRoot { trie.root = hashNode{hash: root[:]} @@ -80,7 +75,6 @@ func New(root common.Hash) *Trie { func NewTestRLPTrie(root common.Hash) *Trie { trie := &Trie{ newHasherFunc: func() *hasher { return newHasher( /*valueNodesRlpEncoded = */ true) }, - hashMap: make(map[common.Hash]node), } if (root != common.Hash{}) && root != EmptyRoot { trie.root = hashNode{hash: root[:]} @@ -515,7 +509,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( // replacing nodes except accounts if !origNok { - t.evictSubtreeFromHashMap(origNode) return true, value } } @@ -538,14 +531,12 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( if matchlen == len(n.Key) || n.Key[matchlen] == 16 { updated, nn = t.insertRecursive(n.Val, key, pos+matchlen, value) if updated { - t.evictNodeFromHashMap(n) n.Val = nn n.ref.len = 0 } newNode = n } else { // Otherwise branch out at the index where they differ. - t.evictNodeFromHashMap(n) var c1 node if len(n.Key) == matchlen+1 { c1 = n.Val @@ -588,7 +579,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( case i1: updated, nn = t.insertRecursive(n.child1, key, pos+1, value) if updated { - t.evictNodeFromHashMap(n) n.child1 = nn n.ref.len = 0 } @@ -596,13 +586,11 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( case i2: updated, nn = t.insertRecursive(n.child2, key, pos+1, value) if updated { - t.evictNodeFromHashMap(n) n.child2 = nn n.ref.len = 0 } newNode = n default: - t.evictNodeFromHashMap(n) var child node if len(key) == pos+1 { child = value @@ -622,7 +610,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( case *fullNode: child := n.Children[key[pos]] if child == nil { - t.evictNodeFromHashMap(n) if len(key) == pos+1 { n.Children[key[pos]] = value } else { @@ -633,7 +620,6 @@ func (t *Trie) insertRecursive(origNode node, key []byte, pos int, value node) ( } else { updated, nn = t.insertRecursive(child, key, pos+1, value) if updated { - t.evictNodeFromHashMap(n) n.Children[key[pos]] = nn n.ref.len = 0 } @@ -760,14 +746,6 @@ func (t *Trie) hook(hex []byte, n node, hash []byte) error { } func (t *Trie) touchAll(n node, hex []byte, del bool, incarnation uint64) { - if del { - t.evictNodeFromHashMap(n) - } else if len(n.reference()) == common.HashLength && debug.IsGetNodeData() { - var key common.Hash - copy(key[:], n.reference()) - t.hashMap[key] = n - } - switch n := n.(type) { case *shortNode: if _, ok := n.Val.(valueNode); !ok { @@ -819,7 +797,6 @@ func (t *Trie) convertToShortNode(child node, pos uint) node { // might not be loaded yet, resolve it just for this // check. if short, ok := child.(*shortNode); ok { - t.evictNodeFromHashMap(child) k := make([]byte, len(short.Key)+1) k[0] = byte(pos) copy(k[1:], short.Key) @@ -854,7 +831,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve } if removeNodeEntirely { - t.evictNodeFromHashMap(n) updated = true touchKey := key[:keyStart+matchlen] if touchKey[len(touchKey)-1] == 16 { @@ -871,7 +847,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve if !updated { newNode = n } else { - t.evictNodeFromHashMap(n) if nn == nil { newNode = nil } else { @@ -905,7 +880,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve if !updated { newNode = n } else { - t.evictNodeFromHashMap(n) if nn == nil { newNode = t.convertToShortNode(n.child2, uint(i2)) } else { @@ -919,7 +893,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve if !updated { newNode = n } else { - t.evictNodeFromHashMap(n) if nn == nil { newNode = t.convertToShortNode(n.child1, uint(i1)) } else { @@ -940,7 +913,6 @@ func (t *Trie) deleteRecursive(origNode node, key []byte, keyStart int, preserve if !updated { newNode = n } else { - t.evictNodeFromHashMap(n) n.Children[key[keyStart]] = nn // Check how many non-nil entries are left after deleting and // reduce the full node to a short node if only one entry is @@ -1078,13 +1050,7 @@ func (t *Trie) Reset() { } func (t *Trie) getHasher() *hasher { - h := t.newHasherFunc() - if debug.IsGetNodeData() { - h.callback = func(key common.Hash, nd node) { - t.hashMap[key] = nd - } - } - return h + return t.newHasherFunc() } // DeepHash returns internal hash of a node reachable by the specified key prefix. @@ -1135,8 +1101,6 @@ func (t *Trie) EvictNode(hex []byte) { // can work with other nodes type } - t.evictSubtreeFromHashMap(nd) - var hn common.Hash if nd == nil { fmt.Printf("nd == nil, hex %x, parent node: %T\n", hex, parent) @@ -1214,65 +1178,3 @@ func (t *Trie) TrieSize() int { func (t *Trie) NumberOfAccounts() int { return calcSubtreeNodes(t.root) } - -// GetNodeByHash gets node's RLP by hash. -func (t *Trie) GetNodeByHash(hash common.Hash) []byte { - nd := t.hashMap[hash] - if nd == nil { - return nil - } - - h := t.getHasher() - defer returnHasherToPool(h) - - rlp, err := h.hashChildren(nd, 0) - if err != nil { - log.Warn("GetNodeByHash error while producing node RLP", "err", err) - return nil - } - - return libcommon.Copy(rlp) -} - -func (t *Trie) evictNodeFromHashMap(nd node) { - if !debug.IsGetNodeData() || nd == nil { - return - } - if len(nd.reference()) == common.HashLength { - var key common.Hash - copy(key[:], nd.reference()) - delete(t.hashMap, key) - } -} - -func (t *Trie) evictSubtreeFromHashMap(n node) { - if !debug.IsGetNodeData() { - return - } - t.evictNodeFromHashMap(n) - - switch n := n.(type) { - case *shortNode: - if _, ok := n.Val.(valueNode); !ok { - t.evictSubtreeFromHashMap(n.Val) - } - case *duoNode: - t.evictSubtreeFromHashMap(n.child1) - t.evictSubtreeFromHashMap(n.child2) - case *fullNode: - for _, child := range n.Children { - if child != nil { - t.evictSubtreeFromHashMap(child) - } - } - case *accountNode: - if n.storage != nil { - t.evictSubtreeFromHashMap(n.storage) - } - } -} - -// HashMapSize returns the number of entries in trie's hash map. -func (t *Trie) HashMapSize() int { - return len(t.hashMap) -} diff --git a/turbo/trie/trie_test.go b/turbo/trie/trie_test.go index be1d4dc70..f4691cb33 100644 --- a/turbo/trie/trie_test.go +++ b/turbo/trie/trie_test.go @@ -27,7 +27,6 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ledgerwatch/erigon/common" - "github.com/ledgerwatch/erigon/common/debug" "github.com/ledgerwatch/erigon/core/types/accounts" "github.com/ledgerwatch/erigon/crypto" "github.com/ledgerwatch/erigon/rlp" @@ -282,45 +281,6 @@ func TestDeepHash(t *testing.T) { } } -func TestHashMapLeak(t *testing.T) { - debug.OverrideGetNodeData(true) - defer debug.OverrideGetNodeData(false) - // freeze the randomness - random := rand.New(rand.NewSource(794656320434)) - - // now create a trie with some small and some big leaves - trie := newEmpty() - nTouches := 256 * 10 - - var key [1]byte - var val [8]byte - for i := 0; i < nTouches; i++ { - key[0] = byte(random.Intn(256)) - binary.BigEndian.PutUint64(val[:], random.Uint64()) - - option := random.Intn(3) - if option == 0 { - // small leaf node - trie.Update(key[:], val[:]) - } else if option == 1 { - // big leaf node - trie.Update(key[:], crypto.Keccak256(val[:])) - } else { - // test delete as well - trie.Delete(key[:]) - } - } - - // check the size of trie's hash map - trie.Hash() - - nHashes := trie.HashMapSize() - nExpected := 1 + 16 + 256/3 - - assert.GreaterOrEqual(t, nHashes, nExpected*7/8) - assert.LessOrEqual(t, nHashes, nExpected*9/8) -} - func genRandomByteArrayOfLen(length uint) []byte { array := make([]byte, length) for i := uint(0); i < length; i++ {