diff --git a/root.go b/root.go new file mode 100644 index 0000000..ed1d83d --- /dev/null +++ b/root.go @@ -0,0 +1,39 @@ +package smt + +import "encoding/binary" + +const ( + // These are intentionally exposed to allow for for testing and custom + // implementations of downstream applications. + SmtRootSizeBytes = 32 + SmstRootSizeBytes = SmtRootSizeBytes + sumSizeBytes + countSizeBytes +) + +// Sum returns the uint64 sum of the merkle root, it checks the length of the +// merkle root and if it is no the same as the size of the SMST's expected +// root hash it will panic. +func (r MerkleRoot) Sum() uint64 { + if len(r)%SmtRootSizeBytes == 0 { + panic("root#sum: not a merkle sum trie") + } + + firstSumByteIdx, firstCountByteIdx := getFirstMetaByteIdx([]byte(r)) + + var sumBz [sumSizeBytes]byte + copy(sumBz[:], []byte(r)[firstSumByteIdx:firstCountByteIdx]) + return binary.BigEndian.Uint64(sumBz[:]) +} + +// Count returns the uint64 count of the merkle root, a cryptographically secure +// count of the number of non-empty leafs in the tree. +func (r MerkleRoot) Count() uint64 { + if len(r)%SmtRootSizeBytes == 0 { + panic("root#sum: not a merkle sum trie") + } + + _, firstCountByteIdx := getFirstMetaByteIdx([]byte(r)) + + var countBz [countSizeBytes]byte + copy(countBz[:], []byte(r)[firstCountByteIdx:]) + return binary.BigEndian.Uint64(countBz[:]) +} diff --git a/smst.go b/smst.go index 89b0c13..b07114b 100644 --- a/smst.go +++ b/smst.go @@ -38,7 +38,7 @@ func NewSparseMerkleSumTrie( hasher hash.Hash, options ...TrieSpecOption, ) *SMST { - trieSpec := newTrieSpec(hasher, true) + trieSpec := NewTrieSpec(hasher, true) for _, option := range options { option(&trieSpec) } diff --git a/smt.go b/smt.go index 73b9c37..bab657c 100644 --- a/smt.go +++ b/smt.go @@ -34,7 +34,7 @@ func NewSparseMerkleTrie( options ...TrieSpecOption, ) *SMT { smt := SMT{ - TrieSpec: newTrieSpec(hasher, false), + TrieSpec: NewTrieSpec(hasher, false), nodes: nodes, } for _, option := range options { diff --git a/trie_spec.go b/trie_spec.go index 304e8fb..bbc38d2 100644 --- a/trie_spec.go +++ b/trie_spec.go @@ -14,12 +14,21 @@ type TrieSpec struct { sumTrie bool } -// newTrieSpec returns a new TrieSpec with the given hasher and sumTrie flag -func newTrieSpec(hasher hash.Hash, sumTrie bool) TrieSpec { +// NewTrieSpec returns a new TrieSpec with the given hasher and sumTrie flag +func NewTrieSpec( + hasher hash.Hash, + sumTrie bool, + opts ...TrieSpecOption, +) TrieSpec { spec := TrieSpec{th: *NewTrieHasher(hasher)} spec.ph = &pathHasher{spec.th} spec.vh = &valueHasher{spec.th} spec.sumTrie = sumTrie + + for _, opt := range opts { + opt(&spec) + } + return spec } @@ -28,6 +37,10 @@ func (spec *TrieSpec) Spec() *TrieSpec { return spec } +// PathHasherSize returns the length (in bytes) of digests produced by the +// path hasher +func (spec *TrieSpec) PathHasherSize() int { return spec.ph.PathSize() } + // placeholder returns the default placeholder value depending on the trie type func (spec *TrieSpec) placeholder() []byte { if spec.sumTrie {