diff --git a/trie/hasher.go b/trie/hasher.go index b6223bf32b..301e879e49 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -49,15 +49,20 @@ func returnHasherToPool(h *hasher) { hasherPool.Put(h) } +// canUnload decides whether a node can be unloaded. +func (n *nodeFlag) canUnload(cachegen, cachelimit uint16, depth int) bool { + return !n.dirty && cachegen-n.gen >= cachelimit/uint16(depth+1) +} + // hash collapses a node down into a hash node, also returning a copy of the // original node initialzied with the computed hash to replace the original one. -func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) { +func (h *hasher) hash(n node, db DatabaseWriter, force bool, depth int) (node, node, error) { // If we're not storing the node, just hashing, use avaialble cached data if hash, dirty := n.cache(); hash != nil { if db == nil { return hash, n, nil } - if n.canUnload(h.cachegen, h.cachelimit) { + if n.canUnload(h.cachegen, h.cachelimit, depth) { // Unload the node from cache. All of its subnodes will have a lower or equal // cache generation number. return hash, hash, nil @@ -67,7 +72,7 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) } } // Trie not processed yet or needs storage, walk the children - collapsed, cached, err := h.hashChildren(n, db) + collapsed, cached, err := h.hashChildren(n, db, depth) if err != nil { return hashNode{}, n, err } @@ -97,7 +102,7 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error) // hashChildren replaces the children of a node with their hashes if the encoded // size of the child is larger than a hash, returning the collapsed node as well // as a replacement for the original node with the child hashes cached in. -func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, error) { +func (h *hasher) hashChildren(original node, db DatabaseWriter, depth int) (node, node, error) { var err error switch n := original.(type) { @@ -108,7 +113,7 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err cached.Key = common.CopyBytes(n.Key) if _, ok := n.Val.(valueNode); !ok { - collapsed.Val, cached.Val, err = h.hash(n.Val, db, false) + collapsed.Val, cached.Val, err = h.hash(n.Val, db, false, depth+1) if err != nil { return original, original, err } @@ -124,7 +129,7 @@ func (h *hasher) hashChildren(original node, db DatabaseWriter) (node, node, err for i := 0; i < 16; i++ { if n.Children[i] != nil { - collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false) + collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false, depth+1) if err != nil { return original, original, err } diff --git a/trie/node.go b/trie/node.go index 4aa0cab65a..859677cd12 100644 --- a/trie/node.go +++ b/trie/node.go @@ -30,7 +30,7 @@ var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b type node interface { fstring(string) string cache() (hashNode, bool) - canUnload(cachegen, cachelimit uint16) bool + canUnload(cachegen, cachelimit uint16, depth int) bool } type ( @@ -62,15 +62,14 @@ type nodeFlag struct { dirty bool // whether the node has changes that must be written to the database } -// canUnload tells whether a node can be unloaded. -func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool { - return !n.dirty && cachegen-n.gen >= cachelimit +func (n *fullNode) canUnload(gen, limit uint16, depth int) bool { + return n.flags.canUnload(gen, limit, depth) } - -func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } -func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) } -func (n hashNode) canUnload(uint16, uint16) bool { return false } -func (n valueNode) canUnload(uint16, uint16) bool { return false } +func (n *shortNode) canUnload(gen, limit uint16, depth int) bool { + return n.flags.canUnload(gen, limit, depth) +} +func (n hashNode) canUnload(uint16, uint16, int) bool { return false } +func (n valueNode) canUnload(uint16, uint16, int) bool { return false } func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty } diff --git a/trie/node_test.go b/trie/node_test.go index 7ad1ff9e7b..4ad5ae0fac 100644 --- a/trie/node_test.go +++ b/trie/node_test.go @@ -51,7 +51,7 @@ func TestCanUnload(t *testing.T) { } for _, test := range tests { - if got := test.flag.canUnload(test.cachegen, test.cachelimit); got != test.want { + if got := test.flag.canUnload(test.cachegen, test.cachelimit, 0); got != test.want { t.Errorf("%+v\n got %t, want %t", test, got, test.want) } } diff --git a/trie/proof.go b/trie/proof.go index bea5e5c098..69f3e8183e 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -75,7 +75,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue { for i, n := range nodes { // Don't bother checking for errors here since hasher panics // if encoding doesn't work and we're not writing to any database. - n, _, _ = hasher.hashChildren(n, nil) + n, _, _ = hasher.hashChildren(n, nil, 0) hn, _ := hasher.store(n, nil, false) if _, ok := hn.(hashNode); ok || i == 0 { // If the node's database encoding is a hash (or is the diff --git a/trie/trie.go b/trie/trie.go index 914bf20fa5..c0c30c39c4 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -495,5 +495,5 @@ func (t *Trie) hashRoot(db DatabaseWriter) (node, node, error) { } h := newHasher(t.cachegen, t.cachelimit) defer returnHasherToPool(h) - return h.hash(t.root, db, true) + return h.hash(t.root, db, true, 0) }