trie: ensure dirty flag is unset for embedded child nodes
This was caught by the new invariant check.
This commit is contained in:
parent
44f419ec0f
commit
8d56bf5ceb
@ -75,23 +75,20 @@ func (h *hasher) hash(n node, db DatabaseWriter, force bool) (node, node, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return hashNode{}, n, err
|
return hashNode{}, n, err
|
||||||
}
|
}
|
||||||
// Cache the hash of the ndoe for later reuse.
|
// Cache the hash of the ndoe for later reuse and remove
|
||||||
if hash, ok := hashed.(hashNode); ok && !force {
|
// the dirty flag in commit mode. It's fine to assign these values directly
|
||||||
switch cached := cached.(type) {
|
// without copying the node first because hashChildren copies it.
|
||||||
|
cachedHash, _ := hashed.(hashNode)
|
||||||
|
switch cn := cached.(type) {
|
||||||
case *shortNode:
|
case *shortNode:
|
||||||
cached = cached.copy()
|
cn.flags.hash = cachedHash
|
||||||
cached.flags.hash = hash
|
|
||||||
if db != nil {
|
if db != nil {
|
||||||
cached.flags.dirty = false
|
cn.flags.dirty = false
|
||||||
}
|
}
|
||||||
return hashed, cached, nil
|
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
cached = cached.copy()
|
cn.flags.hash = cachedHash
|
||||||
cached.flags.hash = hash
|
|
||||||
if db != nil {
|
if db != nil {
|
||||||
cached.flags.dirty = false
|
cn.flags.dirty = false
|
||||||
}
|
|
||||||
return hashed, cached, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hashed, cached, nil
|
return hashed, cached, nil
|
||||||
|
@ -462,32 +462,45 @@ func runRandTest(rt randTest) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case opCheckCacheInvariant:
|
case opCheckCacheInvariant:
|
||||||
return checkCacheInvariant(tr.root, tr.cachegen, 0)
|
return checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCacheInvariant(n node, parentCachegen uint16, depth int) bool {
|
func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) bool {
|
||||||
|
var children []node
|
||||||
|
var flag nodeFlag
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *shortNode:
|
||||||
if n.flags.gen > parentCachegen {
|
flag = n.flags
|
||||||
fmt.Printf("cache invariant violation: %d > %d\nat depth %d node %s", n.flags.gen, parentCachegen, depth, spew.Sdump(n))
|
children = []node{n.Val}
|
||||||
return false
|
|
||||||
}
|
|
||||||
return checkCacheInvariant(n.Val, n.flags.gen, depth+1)
|
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
if n.flags.gen > parentCachegen {
|
flag = n.flags
|
||||||
fmt.Printf("cache invariant violation: %d > %d\nat depth %d node %s", n.flags.gen, parentCachegen, depth, spew.Sdump(n))
|
children = n.Children[:]
|
||||||
return false
|
default:
|
||||||
}
|
|
||||||
for _, child := range n.Children {
|
|
||||||
if !checkCacheInvariant(child, n.flags.gen, depth+1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showerror := func() {
|
||||||
|
fmt.Printf("at depth %d node %s", depth, spew.Sdump(n))
|
||||||
|
fmt.Printf("parent: %s", spew.Sdump(parent))
|
||||||
|
}
|
||||||
|
if flag.gen > parentCachegen {
|
||||||
|
fmt.Printf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen)
|
||||||
|
showerror()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if depth > 0 && !parentDirty && flag.dirty {
|
||||||
|
fmt.Printf("cache invariant violation: child is dirty but parent isn't\n")
|
||||||
|
showerror()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, child := range children {
|
||||||
|
if !checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user