trie: ensure dirty flag is unset for embedded child nodes
This was caught by the new invariant check.
This commit is contained in:
		| @@ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user