Skip to content

Commit

Permalink
Further structure library
Browse files Browse the repository at this point in the history
  • Loading branch information
liamsi committed Jul 31, 2020
1 parent bf9fc51 commit dd6191c
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 102 deletions.
36 changes: 36 additions & 0 deletions namespace/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package namespace

type PrefixedData struct {
namespaceLen int
prefixedData []byte
}

func (n PrefixedData) NamespaceID() ID {
return n.prefixedData[:n.namespaceLen]
}

func (n PrefixedData) Data() []byte {
return n.prefixedData[n.namespaceLen:]
}

func (n PrefixedData) Bytes() []byte {
return n.prefixedData
}

func (n PrefixedData) NamespaceSize() int {
return n.namespaceLen
}

func NewPrefixedData(namespaceLen int, prefixedData []byte) *PrefixedData {
return &PrefixedData{
namespaceLen: namespaceLen,
prefixedData: prefixedData,
}
}

func PrefixedDataFrom(namespace []byte, data []byte) *PrefixedData {
return &PrefixedData{
namespaceLen: len(namespace),
prefixedData: append(namespace, data...),
}
}
13 changes: 13 additions & 0 deletions namespace/id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package namespace

import "bytes"

type ID []byte

func (nid ID) Less(other ID) bool {
return bytes.Compare(nid, other) < 0
}

func (nid ID) Equal(other ID) bool {
return bytes.Equal(nid, other)
}
21 changes: 10 additions & 11 deletions nmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
"github.com/liamsi/merkletree"

"github.com/lazyledger/nmt/internal"
"github.com/lazyledger/nmt/namespace"
"github.com/lazyledger/nmt/treehasher"
// anonymous import to increase the readability
. "github.com/lazyledger/nmt/types"
)

var (
Expand All @@ -22,7 +21,7 @@ type NamespacedMerkleTree struct {
tree *merkletree.Tree

// just cache stuff until we pass in a store and keep all nodes in there
leafs []NamespacePrefixedData
leafs []namespace.PrefixedData
leafHashes [][]byte
// this can be used to efficiently lookup the range for an
// existing namespace without iterating through the leafs
Expand Down Expand Up @@ -58,11 +57,11 @@ func (n NamespacedMerkleTree) Prove(index int) (
// Note that in cases (nID < minNID) or (maxNID < nID) we do not
// generate any proof and we return an empty range (0,0) to
// indicate that this namespace is not contained in the tree.
func (n NamespacedMerkleTree) ProveNamespace(nID NamespaceID) (
func (n NamespacedMerkleTree) ProveNamespace(nID namespace.ID) (
proofStart int,
proofEnd int,
proof [][]byte,
foundLeafs []NamespacePrefixedData,
foundLeafs []namespace.PrefixedData,
leafHashes [][]byte, // XXX: introduce a type/type alias, e.g FlaggedHas
) {
found, proofStart, proofEnd := n.foundNamespaceID(nID)
Expand All @@ -76,7 +75,7 @@ func (n NamespacedMerkleTree) ProveNamespace(nID NamespaceID) (
// Generate a proof for an absence using the
// range the namespace would be in
// TODO: document this as a proper godoc comment
var prevLeaf NamespacePrefixedData
var prevLeaf namespace.PrefixedData
for index, curLeaf := range n.leafs {
if index == 0 {
prevLeaf = curLeaf
Expand Down Expand Up @@ -127,7 +126,7 @@ func (n NamespacedMerkleTree) ProveNamespace(nID NamespaceID) (
return proofStart, proofEnd, proof, nil, n.leafHashes[proofStart:proofEnd]
}

func (n *NamespacedMerkleTree) foundNamespaceID(nID NamespaceID) (bool, int, int) {
func (n *NamespacedMerkleTree) foundNamespaceID(nID namespace.ID) (bool, int, int) {
foundRng, found := n.namespaceRanges[string(nID)]
// XXX casting from uint64 to int is kinda crappy but nebolousLabs'
// range proof api requires int params only to convert them to uint64 ...
Expand All @@ -144,7 +143,7 @@ func (n NamespacedMerkleTree) NamespaceSize() int {
// Returns an error if the namespace ID size of the input
// does not match the tree's NamespaceSize() or the leafs are not pushed in
// order (i.e. lexicographically sorted by namespace ID).
func (n *NamespacedMerkleTree) Push(data NamespacePrefixedData) error {
func (n *NamespacedMerkleTree) Push(data namespace.PrefixedData) error {
got, want := data.NamespaceSize(), n.NamespaceSize()
if got != want {
return fmt.Errorf("%w: got: %v, want: %v", ErrMismatchedNamespaceSize, got, want)
Expand All @@ -171,8 +170,8 @@ func (n *NamespacedMerkleTree) Push(data NamespacePrefixedData) error {
// Return the namespaced Merkle Tree's root together with the
// min. and max. namespace ID.
func (n *NamespacedMerkleTree) Root() (
minNs NamespaceID,
maxNs NamespaceID,
minNs namespace.ID,
maxNs namespace.ID,
root []byte,
) {
if len(n.leafs) == 0 {
Expand Down Expand Up @@ -214,7 +213,7 @@ func New(treeHasher treehasher.NmTreeHasher) *NamespacedMerkleTree {
// knows exactly how many leafs will be pushed this will save allocations
// In fact, in that case the caller could pass in the whole data at once
// and we could even use the passed in slice without allocating space for a copy.
leafs: make([]NamespacePrefixedData, 0, 100),
leafs: make([]namespace.PrefixedData, 0, 100),
leafHashes: make([][]byte, 0, 100),
namespaceRanges: make(map[string]merkletree.LeafRange),
tree: merkletree.NewFromTreehasher(treeHasher),
Expand Down
75 changes: 37 additions & 38 deletions nmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
"reflect"
"testing"

"github.com/lazyledger/nmt/namespace"
"github.com/lazyledger/nmt/treehasher/defaulthasher"
// anonymous import only to improve readability
. "github.com/lazyledger/nmt/types"
)

const (
Expand All @@ -22,15 +21,15 @@ func TestFromNamespaceAndData(t *testing.T) {
name string
namespace []byte
data []byte
want *NamespacePrefixedData
want *namespace.PrefixedData
}{
0: {"simple case", []byte("namespace1"), []byte("data1"), FromPrefixedData(10, append([]byte("namespace1"), []byte("data1")...))},
1: {"simpler case", []byte("1"), []byte("d"), FromPrefixedData(1, append([]byte("1"), []byte("d")...))},
0: {"simple case", []byte("namespace1"), []byte("data1"), namespace.NewPrefixedData(10, append([]byte("namespace1"), []byte("data1")...))},
1: {"simpler case", []byte("1"), []byte("d"), namespace.NewPrefixedData(1, append([]byte("1"), []byte("d")...))},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := FromNamespaceAndData(tt.namespace, tt.data); !reflect.DeepEqual(got, tt.want) {
t.Errorf("FromNamespaceAndData() = %v, want %v", got, tt.want)
if got := namespace.PrefixedDataFrom(tt.namespace, tt.data); !reflect.DeepEqual(got, tt.want) {
t.Errorf("PrefixedDataFrom() = %v, want %v", got, tt.want)
}
})
}
Expand All @@ -39,22 +38,22 @@ func TestFromNamespaceAndData(t *testing.T) {
func TestNamespacedMerkleTree_Push(t *testing.T) {
tests := []struct {
name string
data NamespacePrefixedData
data namespace.PrefixedData
wantErr bool
}{
{"1st push: always OK", *FromNamespaceAndData([]byte{0, 0, 0}, []byte("dummy data")), false},
{"push with same namespace: OK", *FromNamespaceAndData([]byte{0, 0, 0}, []byte("dummy data")), false},
{"push with greater namespace: OK", *FromNamespaceAndData([]byte{0, 0, 1}, []byte("dummy data")), false},
{"push with smaller namespace: Err", *FromNamespaceAndData([]byte{0, 0, 0}, []byte("dummy data")), true},
{"push with same namespace: Ok", *FromNamespaceAndData([]byte{0, 0, 1}, []byte("dummy data")), false},
{"push with greater namespace: Ok", *FromNamespaceAndData([]byte{1, 0, 0}, []byte("dummy data")), false},
{"push with smaller namespace: Err", *FromNamespaceAndData([]byte{0, 0, 1}, []byte("dummy data")), true},
{"push with smaller namespace: Err", *FromNamespaceAndData([]byte{0, 0, 0}, []byte("dummy data")), true},
{"push with smaller namespace: Err", *FromNamespaceAndData([]byte{0, 1, 0}, []byte("dummy data")), true},
{"push with same as last namespace: OK", *FromNamespaceAndData([]byte{1, 0, 0}, []byte("dummy data")), false},
{"push with greater as last namespace: OK", *FromNamespaceAndData([]byte{1, 1, 0}, []byte("dummy data")), false},
{"1st push: always OK", *namespace.PrefixedDataFrom([]byte{0, 0, 0}, []byte("dummy data")), false},
{"push with same namespace: OK", *namespace.PrefixedDataFrom([]byte{0, 0, 0}, []byte("dummy data")), false},
{"push with greater namespace: OK", *namespace.PrefixedDataFrom([]byte{0, 0, 1}, []byte("dummy data")), false},
{"push with smaller namespace: Err", *namespace.PrefixedDataFrom([]byte{0, 0, 0}, []byte("dummy data")), true},
{"push with same namespace: Ok", *namespace.PrefixedDataFrom([]byte{0, 0, 1}, []byte("dummy data")), false},
{"push with greater namespace: Ok", *namespace.PrefixedDataFrom([]byte{1, 0, 0}, []byte("dummy data")), false},
{"push with smaller namespace: Err", *namespace.PrefixedDataFrom([]byte{0, 0, 1}, []byte("dummy data")), true},
{"push with smaller namespace: Err", *namespace.PrefixedDataFrom([]byte{0, 0, 0}, []byte("dummy data")), true},
{"push with smaller namespace: Err", *namespace.PrefixedDataFrom([]byte{0, 1, 0}, []byte("dummy data")), true},
{"push with same as last namespace: OK", *namespace.PrefixedDataFrom([]byte{1, 0, 0}, []byte("dummy data")), false},
{"push with greater as last namespace: OK", *namespace.PrefixedDataFrom([]byte{1, 1, 0}, []byte("dummy data")), false},
// note this tests for another kind of error: ErrMismatchedNamespaceSize
{"push with wrong namespace size: Err", *FromNamespaceAndData([]byte{1, 1, 0, 0}, []byte("dummy data")), true},
{"push with wrong namespace size: Err", *namespace.PrefixedDataFrom([]byte{1, 1, 0, 0}, []byte("dummy data")), true},
}
n := New(defaulthasher.New(3, crypto.SHA256))
for _, tt := range tests {
Expand Down Expand Up @@ -82,17 +81,17 @@ func TestNamespacedMerkleTreeRoot(t *testing.T) {
tests := []struct {
name string
nidLen int
pushedData []NamespacePrefixedData
wantMinNs NamespaceID
wantMaxNs NamespaceID
pushedData []namespace.PrefixedData
wantMinNs namespace.ID
wantMaxNs namespace.ID
wantRoot []byte
}{
// default empty root according to base case:
// https://github.com/lazyledger/lazyledger-specs/blob/master/specs/data_structures.md#namespace-merkle-tree
{"Empty", 3, nil, zeroNs, zeroNs, emptyRoot},
{"One leaf", 3, []NamespacePrefixedData{*FromNamespaceAndData(zeroNs, leaf)}, zeroNs, zeroNs, leafHash},
{"Two leafs", 3, []NamespacePrefixedData{*FromNamespaceAndData(zeroNs, leaf), *FromNamespaceAndData(zeroNs, leaf)}, zeroNs, zeroNs, twoZeroLeafsRoot},
{"Two leafs diff namespaces", 3, []NamespacePrefixedData{*FromNamespaceAndData(zeroNs, leaf), *FromNamespaceAndData(onesNS, leaf)}, zeroNs, onesNS, diffNSLeafsRoot},
{"One leaf", 3, []namespace.PrefixedData{*namespace.PrefixedDataFrom(zeroNs, leaf)}, zeroNs, zeroNs, leafHash},
{"Two leafs", 3, []namespace.PrefixedData{*namespace.PrefixedDataFrom(zeroNs, leaf), *namespace.PrefixedDataFrom(zeroNs, leaf)}, zeroNs, zeroNs, twoZeroLeafsRoot},
{"Two leafs diff namespaces", 3, []namespace.PrefixedData{*namespace.PrefixedDataFrom(zeroNs, leaf), *namespace.PrefixedDataFrom(onesNS, leaf)}, zeroNs, onesNS, diffNSLeafsRoot},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -120,43 +119,43 @@ func TestNamespacedMerkleTree_ProveNamespace_Ranges(t *testing.T) {
tests := []struct {
name string
nidLen int
pushData []NamespacePrefixedData
proveNID NamespaceID
pushData []namespace.PrefixedData
proveNID namespace.ID
wantProofStart int
wantProofEnd int
wantFound bool
}{
{"found", 1,
[]NamespacePrefixedData{*FromPrefixedData(1, []byte("0_data"))}, []byte("0"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(1, []byte("0_data"))}, []byte("0"),
0, 1, true},
{"not found", 1,
[]NamespacePrefixedData{*FromPrefixedData(1, []byte("0_data"))}, []byte("1"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(1, []byte("0_data"))}, []byte("1"),
0, 0, false},
{"two leafs and found", 1,
[]NamespacePrefixedData{*FromPrefixedData(1, []byte("0_data")), *FromPrefixedData(1, []byte("1_data"))}, []byte("1"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(1, []byte("0_data")), *namespace.NewPrefixedData(1, []byte("1_data"))}, []byte("1"),
1, 2, true},
{"two leafs and found", 1,
[]NamespacePrefixedData{*FromPrefixedData(1, []byte("0_data")), *FromPrefixedData(1, []byte("0_data"))}, []byte("1"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(1, []byte("0_data")), *namespace.NewPrefixedData(1, []byte("0_data"))}, []byte("1"),
0, 0, false},
{"three leafs and found", 1,
[]NamespacePrefixedData{*FromPrefixedData(1, []byte("0_data")), *FromPrefixedData(1, []byte("0_data")), *FromPrefixedData(1, []byte("1_data"))}, []byte("1"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(1, []byte("0_data")), *namespace.NewPrefixedData(1, []byte("0_data")), *namespace.NewPrefixedData(1, []byte("1_data"))}, []byte("1"),
2, 3, true},
{"three leafs and not found but with range", 2,
[]NamespacePrefixedData{*FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("11_data"))}, []byte("01"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("11_data"))}, []byte("01"),
2, 3, false},
{"three leafs and not found but within range", 2,
[]NamespacePrefixedData{*FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("11_data"))}, []byte("01"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("11_data"))}, []byte("01"),
2, 3, false},
{"4 leafs and not found but within range", 2,
[]NamespacePrefixedData{*FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("11_data")), *FromPrefixedData(2, []byte("11_data"))}, []byte("01"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("11_data")), *namespace.NewPrefixedData(2, []byte("11_data"))}, []byte("01"),
2, 3, false},
// In the cases (nID < minNID) or (maxNID < nID) we do not generate any proof
// and the (minNS, maxNs, root) should be indication enough that nID is not in that range.
{"4 leafs, not found and nID < minNID", 2,
[]NamespacePrefixedData{*FromPrefixedData(2, []byte("01_data")), *FromPrefixedData(2, []byte("01_data")), *FromPrefixedData(2, []byte("01_data")), *FromPrefixedData(2, []byte("11_data"))}, []byte("00"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(2, []byte("01_data")), *namespace.NewPrefixedData(2, []byte("01_data")), *namespace.NewPrefixedData(2, []byte("01_data")), *namespace.NewPrefixedData(2, []byte("11_data"))}, []byte("00"),
0, 0, false},
{"4 leafs, not found and nID > maxNID ", 2,
[]NamespacePrefixedData{*FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("00_data")), *FromPrefixedData(2, []byte("01_data")), *FromPrefixedData(2, []byte("01_data"))}, []byte("11"),
[]namespace.PrefixedData{*namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("00_data")), *namespace.NewPrefixedData(2, []byte("01_data")), *namespace.NewPrefixedData(2, []byte("01_data"))}, []byte("11"),
0, 0, false},
}
for _, tt := range tests {
Expand Down
1 change: 1 addition & 0 deletions proofs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package nmt
4 changes: 2 additions & 2 deletions treehasher/defaulthasher/hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"bytes"
"crypto"

"github.com/lazyledger/nmt/namespace"
"github.com/lazyledger/nmt/treehasher"
"github.com/lazyledger/nmt/types"
)

const (
Expand All @@ -31,7 +31,7 @@ func New(nidLen int, baseHasher crypto.Hash) *DefaultNamespacedTreeHasher {
}
}

func (n *DefaultNamespacedTreeHasher) EmptyRoot() (minNs, maxNs types.NamespaceID, root []byte) {
func (n *DefaultNamespacedTreeHasher) EmptyRoot() (minNs, maxNs namespace.ID, root []byte) {
emptyNs := bytes.Repeat([]byte{0}, n.NamespaceLen)
placeHolderHash := bytes.Repeat([]byte{0}, n.Size())
return emptyNs, emptyNs, placeHolderHash
Expand Down
4 changes: 2 additions & 2 deletions treehasher/nmtreehasher.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package treehasher

import (
"github.com/lazyledger/nmt/types"
"github.com/lazyledger/nmt/namespace"
)

// TODO: make all methods "namespaced" too (like EmptyRoot())
type NmTreeHasher interface {
// EmptyRoot returns the namespaced root for a no-leafs Namespaced Merkle tree.
// This can be used to define whatever
EmptyRoot() (minNs, maxNs types.NamespaceID, root []byte)
EmptyRoot() (minNs, maxNs namespace.ID, root []byte)

// HashLeaf defines how a leaf is hashed.
HashLeaf(leaf []byte) []byte
Expand Down
36 changes: 0 additions & 36 deletions types/data.go

This file was deleted.

13 changes: 0 additions & 13 deletions types/namespaceid.go

This file was deleted.

0 comments on commit dd6191c

Please sign in to comment.