Skip to content

Commit

Permalink
Merge branch 'master' into adds-size-method
Browse files Browse the repository at this point in the history
  • Loading branch information
staheri14 committed May 4, 2023
2 parents ab2d666 + d0789c6 commit b78ba42
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 24 deletions.
13 changes: 10 additions & 3 deletions hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const (
var _ hash.Hash = (*Hasher)(nil)

var (
ErrUnorderedSiblings = errors.New("NMT sibling nodes should be ordered lexicographically by namespace IDs")
ErrInvalidNodeLen = errors.New("invalid NMT node size")
ErrInvalidLeafLen = errors.New("invalid NMT leaf size")
ErrUnorderedSiblings = errors.New("NMT sibling nodes should be ordered lexicographically by namespace IDs")
ErrInvalidNodeLen = errors.New("invalid NMT node size")
ErrInvalidLeafLen = errors.New("invalid NMT leaf size")
ErrInvalidNodeNamespaceOrder = errors.New("invalid NMT node namespace order")
)

type Hasher struct {
Expand Down Expand Up @@ -201,6 +202,12 @@ func (n *Hasher) ValidateNodeFormat(node []byte) (err error) {
if nodeLen != expectedNodeLen {
return fmt.Errorf("%w: got: %v, want %v", ErrInvalidNodeLen, nodeLen, expectedNodeLen)
}
// check the namespace order
minNID := namespace.ID(MinNamespace(node, n.NamespaceSize()))
maxNID := namespace.ID(MaxNamespace(node, n.NamespaceSize()))
if maxNID.Less(minNID) {
return fmt.Errorf("%w: max namespace ID %d is less than min namespace ID %d ", ErrInvalidNodeNamespaceOrder, maxNID, minNID)
}
return nil
}

Expand Down
138 changes: 121 additions & 17 deletions hasher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,6 @@ func Test_namespacedTreeHasher_HashNode(t *testing.T) {
concat([]byte{0, 0, 0, 0}, randHash),
concat([]byte{0, 0, 1, 1}, randHash))),
},
// XXX: can this happen in practice? or is this an invalid state?
{
"leftmin>rightmin && leftmax<rightmax", 2,
children{
concat([]byte{1, 1, 0, 0}, randHash),
concat([]byte{0, 0, 0, 1}, randHash),
},
concat([]byte{0, 0, 0, 1}, // minNID||maxNID
sum(crypto.SHA256, []byte{NodePrefix}, // Hash(NodePrefix||left||right)
concat([]byte{1, 1, 0, 0}, randHash),
concat([]byte{0, 0, 0, 1}, randHash))),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -221,7 +209,8 @@ func TestNamespaceHasherSum(t *testing.T) {
}
}

func TestHashNode_ChildrenNamespaceRange(t *testing.T) {
// TestHashNode verifies the HashNode function for scenarios where it is expected to produce errors, as well as those where it is not.
func TestHashNode_Error(t *testing.T) {
// create a dummy hash to use as the digest of the left and right child
randHash := createByteSlice(sha256.Size, 0x01)
type children struct {
Expand All @@ -237,7 +226,7 @@ func TestHashNode_ChildrenNamespaceRange(t *testing.T) {
errType error
}{
{
"left.maxNs>right.minNs", 2,
"unordered siblings: left.maxNs>right.minNs", 2,
children{
concat([]byte{0, 0, 1, 1}, randHash),
concat([]byte{0, 0, 1, 1}, randHash),
Expand All @@ -246,7 +235,7 @@ func TestHashNode_ChildrenNamespaceRange(t *testing.T) {
ErrUnorderedSiblings,
},
{
"left.maxNs=right.minNs", 2,
"ordered siblings: left.maxNs=right.minNs", 2,
children{
concat([]byte{0, 0, 1, 1}, randHash),
concat([]byte{1, 1, 2, 2}, randHash),
Expand All @@ -255,14 +244,32 @@ func TestHashNode_ChildrenNamespaceRange(t *testing.T) {
nil,
},
{
"left.maxNs<right.minNs", 2,
"ordered siblings: left.maxNs<right.minNs", 2,
children{
concat([]byte{0, 0, 1, 1}, randHash),
concat([]byte{2, 2, 3, 3}, randHash),
},
false,
nil,
},
{
"invalid left sibling format: left.minNs>left.maxNs", 2,
children{
concat([]byte{2, 2, 0, 0}, randHash),
concat([]byte{1, 1, 4, 4}, randHash),
},
true,
ErrInvalidNodeNamespaceOrder,
},
{
"invalid right sibling format: right.minNs>right.maxNs", 2,
children{
concat([]byte{0, 0, 1, 1}, randHash),
concat([]byte{4, 4, 1, 1}, randHash),
},
true,
ErrInvalidNodeNamespaceOrder,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -276,7 +283,7 @@ func TestHashNode_ChildrenNamespaceRange(t *testing.T) {
}
}

func TestValidateSiblingsNamespaceOrder(t *testing.T) {
func TestValidateSiblings(t *testing.T) {
// create a dummy hash to use as the digest of the left and right child
randHash := createByteSlice(sha256.Size, 0x01)

Expand Down Expand Up @@ -375,6 +382,33 @@ func TestValidateNodeFormat(t *testing.T) {
true,
ErrInvalidNodeLen,
},
{
"invalid node: minNS > maxNs",
2,
[]byte{3, 3},
[]byte{1, 1},
concat(hashValue),
true,
ErrInvalidNodeNamespaceOrder,
},
{
"valid node: minNs = maxNs",
2,
minNID,
minNID,
concat(hashValue),
false,
nil,
},
{
"valid node: minNs < maxNs",
2,
minNID,
maxNID,
concat(hashValue),
false,
nil,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -735,3 +769,73 @@ func Test_MustHashLeaf_Panic(t *testing.T) {
})
}
}

func TestMax(t *testing.T) {
tt := []struct {
name string
ns []byte
ns2 []byte
expected []byte
}{
{
"First argument is larger",
[]byte{1, 2, 3},
[]byte{1, 2},
[]byte{1, 2, 3},
},
{
"Second argument is larger",
[]byte{1, 2},
[]byte{1, 2, 3},
[]byte{1, 2, 3},
},
{
"Arguments are equal",
[]byte{1, 2, 3},
[]byte{1, 2, 3},
[]byte{1, 2, 3},
},
}

for _, ts := range tt {
t.Run(ts.name, func(t *testing.T) {
maxResult := max(ts.ns, ts.ns2)
assert.Equal(t, ts.expected, maxResult)
})
}
}

func TestMin(t *testing.T) {
tt := []struct {
name string
ns []byte
ns2 []byte
expected []byte
}{
{
"First argument is smaller",
[]byte{1, 2},
[]byte{1, 2, 3},
[]byte{1, 2},
},
{
"Second argument is smaller",
[]byte{1, 2, 3},
[]byte{1, 2},
[]byte{1, 2},
},
{
"Arguments are equal",
[]byte{1, 2, 3},
[]byte{1, 2, 3},
[]byte{1, 2, 3},
},
}

for _, ts := range tt {
t.Run(ts.name, func(t *testing.T) {
minResult := min(ts.ns, ts.ns2)
assert.Equal(t, ts.expected, minResult)
})
}
}
13 changes: 9 additions & 4 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,17 @@ func TestVerifyLeafHashes_Err(t *testing.T) {
root, err := nmt.Root()
require.NoError(t, err)

// shrink the size of the root so that the root hash is invalid.
corruptRoot := root[:len(root)-1]

// create an NMT proof
nID5 := namespace.ID{5, 5}
proof5, err := nmt.ProveNamespace(nID5)
require.NoError(t, err)
// corrupt the leafHash so that the proof verification fails during the root computation.
// note that the leaf at index 4 has the namespace ID of 5.
leafHash5 := nmt.leafHashes[4][:nmt.NamespaceSize()]
leafHash5 := nmt.leafHashes[4]
corruptLeafHash5 := leafHash5[:nmt.NamespaceSize()]

// corrupt the leafHash: replace its namespace ID with a different one.
nID3 := createByteSlice(nameIDSize, 3)
Expand Down Expand Up @@ -339,7 +343,8 @@ func TestVerifyLeafHashes_Err(t *testing.T) {
root []byte
wantErr bool
}{
{"wrong leafHash: not namespaced", proof5, hasher, true, nID5, [][]byte{leafHash5}, root, true},
{"corrupt root", proof5, hasher, true, nID5, [][]byte{leafHash5}, corruptRoot, true},
{"wrong leafHash: not namespaced", proof5, hasher, true, nID5, [][]byte{corruptLeafHash5}, root, true},
{"wrong leafHash: smaller namespace", proof5, hasher, true, nID5, [][]byte{leafHash5SmallerNID}, root, true},
{"wong leafHash: bigger namespace", proof5, hasher, true, nID5, [][]byte{leafHash5BiggerNID}, root, true},
{"wrong proof.nodes: the last node has an incorrect format", proof4InvalidNodes, hasher, false, nID4, [][]byte{leafHash4}, root, true},
Expand Down Expand Up @@ -507,8 +512,8 @@ func TestVerifyNamespace_False(t *testing.T) {
args args
result bool
}{
{"nID size of proof < nID size of VerifyNamespace's nmt hasher", proof4_1, args{hasher, nid4_2, [][]byte{leaf}, root2}, false},
{"nID size of proof > nID size of VerifyNamespace's nmt hasher", proof4_2, args{hasher, nid4_1, [][]byte{leaf}, root1}, false},
{"nID size of proof.nodes < nID size of VerifyNamespace's nmt hasher", proof4_1, args{hasher, nid4_2, [][]byte{leaf}, root2}, false},
{"nID size of proof.nodes > nID size of VerifyNamespace's nmt hasher", proof4_2, args{hasher, nid4_1, [][]byte{leaf}, root1}, false},
{"nID size of root < nID size of VerifyNamespace's nmt hasher", proof4_2, args{hasher, nid4_2, [][]byte{leaf}, root1}, false},
{"nID size of root > nID size of VerifyNamespace's nmt hasher", proof4_1, args{hasher, nid4_1, [][]byte{leaf}, root2}, false},
{"nID size of proof.leafHash < nID size of VerifyNamespace's nmt hasher", absenceProof9_2, args{hasher, nid9_2, [][]byte{}, root2}, false},
Expand Down

0 comments on commit b78ba42

Please sign in to comment.