Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add node hashing to node tests #811

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go/database/mpt/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ func (n *BranchNode) Check(source NodeSource, thisRef *NodeReference, _ []Nibble
childIsFrozen := handle.Get().IsFrozen()
handle.Release()

// rule: flag -> childIsFrozen (implication)
// rule: child is marked as frozen -> childIsFrozen (implication)
if flag := n.isChildFrozen(byte(i)); flag && !childIsFrozen {
errs = append(errs, fmt.Errorf("in node %v the frozen flag for child 0x%X is invalid, flag: %t, actual: %t", thisRef.Id(), i, flag, childIsFrozen))
}
Expand Down Expand Up @@ -1990,7 +1990,7 @@ func (n *ValueNode) Check(source NodeSource, thisRef *NodeReference, path []Nibb
}

func (n *ValueNode) Dump(out io.Writer, source NodeSource, thisRef *NodeReference, indent string) error {
fmt.Fprintf(out, "%sValue (ID: %v/%t/%d, Hash: %v, hashState: %v): %v - %v\n", indent, thisRef.Id(), n.IsFrozen(), n.pathLength, formatHashForDump(n.hash), n.getHashStatus(), n.key, n.value)
fmt.Fprintf(out, "%sValue (ID: %v/%t/%d, Hash: %v, hashState: %v): %v - %x\n", indent, thisRef.Id(), n.IsFrozen(), n.pathLength, formatHashForDump(n.hash), n.getHashStatus(), n.key, n.value)
return nil
}

Expand Down
74 changes: 65 additions & 9 deletions go/database/mpt/nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
)

var PathLengthTracking = MptConfig{
Hashing: EthereumLikeHashing,
TrackSuffixLengthsInLeafNodes: true,
}

Expand Down Expand Up @@ -1154,6 +1155,10 @@ func TestBranchNode_VisitAbortByChild(t *testing.T) {
}

func TestBranchNode_CheckDetectsIssues(t *testing.T) {
emptyValueHash := common.Hash{
202, 105, 210, 237, 104, 137, 30, 166, 212, 211, 73, 237, 177, 164, 110, 101,
102, 128, 108, 18, 151, 209, 112, 197, 142, 218, 9, 115, 49, 58, 100, 135,
}
unknownHashStatus := hashStatusUnknown
tests := map[string]struct {
setup NodeDesc
Expand All @@ -1164,11 +1169,11 @@ func TestBranchNode_CheckDetectsIssues(t *testing.T) {
"two children": {&Branch{children: Children{1: &Value{}, 2: &Value{}}}, true},
"invalid hash": {&Branch{
children: Children{1: &Value{}, 2: &Value{}},
childHashes: ChildHashes{1: common.Hash{1}, 2: common.Hash{}}, // all hashes are 0 in tests
childHashes: ChildHashes{1: common.Hash{1}, 2: emptyValueHash},
}, false},
"dirty hashes are ignored": {&Branch{
children: Children{1: &Value{}, 2: &Value{}},
childHashes: ChildHashes{1: common.Hash{1}, 2: common.Hash{}}, // all hashes are 0 in tests
childHashes: ChildHashes{1: common.Hash{1}, 2: emptyValueHash},
dirty: true,
dirtyHash: true,
dirtyChildHashes: []int{1},
Expand Down Expand Up @@ -2410,8 +2415,8 @@ func TestExtensionNode_CheckDetectsIssues(t *testing.T) {
"ok": {&Extension{path: []Nibble{1, 2, 3}, next: &Branch{}}, true},
"empty path": {&Extension{next: &Branch{}}, false},
"next not a branch": {&Extension{path: []Nibble{1, 2, 3}, next: &Value{}}, false},
"invalid hash": {&Extension{path: []Nibble{1, 2, 3}, next: &Branch{}, nextHash: common.Hash{1}}, false},
"dirty hash is ignored": {&Extension{path: []Nibble{1, 2, 3}, next: &Branch{}, nextHash: common.Hash{1}, dirty: true, hashDirty: true, nextHashDirty: true}, true},
"invalid hash": {&Extension{path: []Nibble{1, 2, 3}, next: &Branch{}, nextHash: &common.Hash{1}}, false},
"dirty hash is ignored": {&Extension{path: []Nibble{1, 2, 3}, next: &Branch{}, nextHash: &common.Hash{1}, dirty: true, hashDirty: true, nextHashDirty: true}, true},
"full_frozen": {&Extension{frozen: true, path: []Nibble{1, 2, 3}, next: &Branch{frozen: true}}, true},
"partial_frozen": {&Extension{frozen: false, path: []Nibble{1, 2, 3}, next: &Branch{frozen: true}}, true},
"inconsistent_frozen": {&Extension{frozen: true, path: []Nibble{1, 2, 3}, next: &Branch{frozen: false}}, false},
Expand Down Expand Up @@ -3676,6 +3681,7 @@ func TestAccountNode_CheckDetectsIssues(t *testing.T) {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
config := MptConfig{
Hashing: EthereumLikeHashing,
TrackSuffixLengthsInLeafNodes: true,
}
ctxt := newNodeContextWithConfig(t, ctrl, config)
Expand Down Expand Up @@ -4422,6 +4428,7 @@ func TestValueNode_CheckDetectsIssues(t *testing.T) {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
config := MptConfig{
Hashing: EthereumLikeHashing,
TrackSuffixLengthsInLeafNodes: true,
}
ctxt := newNodeContextWithConfig(t, ctrl, config)
Expand Down Expand Up @@ -6392,6 +6399,13 @@ func TestTransitions_MutableTransitionHaveExpectedEffectWithTrackedPrefixLength(
testTransitions_MutableTransitionHaveExpectedEffect(t, config)
}

func TestTransitions_MutableTransitionHaveExpectedEffectWithEthereumHashing(t *testing.T) {
config := S4LiveConfig
config.TrackSuffixLengthsInLeafNodes = true
config.Hashing = EthereumLikeHashing
testTransitions_MutableTransitionHaveExpectedEffect(t, config)
}

func testTransitions_MutableTransitionHaveExpectedEffect(t *testing.T, config MptConfig) {
for _, transition := range getTestTransitions() {
transition := transition
Expand Down Expand Up @@ -6538,6 +6552,13 @@ func TestTransitions_ImmutableTransitionHaveExpectedEffectWithTrackedPrefixLengt
testTransitions_ImmutableTransitionHaveExpectedEffect(t, config)
}

func TestTransitions_ImmutableTransitionHaveExpectedEffectWithEthereumHashing(t *testing.T) {
config := S4LiveConfig
config.TrackSuffixLengthsInLeafNodes = true
config.Hashing = EthereumLikeHashing
testTransitions_ImmutableTransitionHaveExpectedEffect(t, config)
}

func testTransitions_ImmutableTransitionHaveExpectedEffect(t *testing.T, config MptConfig) {
for _, transition := range getTestTransitions() {
transition := transition
Expand Down Expand Up @@ -7459,8 +7480,9 @@ func (b *Branch) Build(ctx *nodeContext) (NodeReference, *shared.Shared[Node]) {
res.nodeBase.clean = !b.dirty
res.frozen = b.frozen
for i, desc := range b.children {
id, _ := ctx.Build(desc)
res.children[i] = id
ref, _ := ctx.Build(desc)
res.children[i] = ref
res.hashes[i], _ = ctx.getHashFor(&ref)
}
for i, hash := range b.childHashes {
res.hashes[i] = hash
Expand All @@ -7487,7 +7509,7 @@ type Extension struct {
path []Nibble
next NodeDesc
hashDirty bool
nextHash common.Hash
nextHash *common.Hash
nextHashDirty bool
hashStatus *hashStatus // overrides dirtyHash flag if set
}
Expand All @@ -7506,8 +7528,12 @@ func (e *Extension) Build(ctx *nodeContext) (NodeReference, *shared.Shared[Node]
if e.hashStatus != nil {
res.hashStatus = *e.hashStatus
}
res.nextHash = e.nextHash
res.nextHashDirty = e.nextHashDirty
if e.nextHash != nil {
res.nextHash = *e.nextHash
} else if !res.nextHashDirty {
res.nextHash, _ = ctx.getHashFor(&res.next)
}
return ref, shared.MakeShared[Node](res)
}

Expand Down Expand Up @@ -7579,7 +7605,22 @@ func newNodeContextWithConfig(t *testing.T, ctrl *gomock.Controller, config MptC
config: config,
}
res.EXPECT().getConfig().AnyTimes().Return(config)
res.EXPECT().getHashFor(gomock.Any()).AnyTimes().Return(common.Hash{}, nil)
res.EXPECT().hashAddress(gomock.Any()).AnyTimes().DoAndReturn(common.Keccak256ForAddress)
res.EXPECT().hashKey(gomock.Any()).AnyTimes().DoAndReturn(common.Keccak256ForKey)
res.EXPECT().getHashFor(gomock.Any()).AnyTimes().DoAndReturn(func(ref *NodeReference) (common.Hash, error) {
// Mock nodes have a constant hash of zero.
handle, err := res.getViewAccess(ref)
if err != nil {
return common.Hash{}, err
}
defer handle.Release()
if _, isMock := handle.Get().(*MockNode); isMock {
return common.Hash{}, nil
}
// All others are hashed according to the configuration.
hasher := config.Hashing.createHasher()
return hasher.getHash(ref, res)
})

// The empty node is always present.
res.Build(Empty{})
Expand Down Expand Up @@ -7675,6 +7716,21 @@ func (c *nodeContext) Build(desc NodeDesc) (NodeReference, *shared.Shared[Node])
})
c.index[ref.Id()] = entry{ref, node}
c.cache[desc] = entry{ref, node}

view := node.GetViewHandle()
wantsHashDirty := true
if _, isMock := view.Get().(*MockNode); !isMock {
_, wantsHashDirty = view.Get().GetHash()
}
view.Release()

if !wantsHashDirty {
hash, _ := c.getHashFor(&ref)
write := node.GetWriteHandle()
write.Get().SetHash(hash)
write.Release()
}

return ref, node
}

Expand Down
Loading