Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Commit

Permalink
names cleanup, readme update
Browse files Browse the repository at this point in the history
rename BaseSMT => TreeSpec

{New,Import}{SMT => SparseMerkleTree}
  • Loading branch information
roysc committed Feb 8, 2023
1 parent ea93a97 commit 676615b
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 99 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ import (
)

func main() {
// Initialise two new key-value store to store the nodes and values of the tree
// Initialise two new key-value store to store the nodes of the tree
// (Note: the tree only stores hashed values, not raw value data)
nodeStore := smt.NewSimpleMap()
valueStore := smt.NewSimpleMap()
// Initialise the tree
tree := smt.NewSparseMerkleTree(nodeStore, valueStore, sha256.New())
tree := smt.NewSMT(nodeStore, sha256.New())

// Update the key "foo" with the value "bar"
_, _ = tree.Update([]byte("foo"), []byte("bar"))
_ = tree.Update([]byte("foo"), []byte("bar"))

// Generate a Merkle proof for foo=bar
proof, _ := tree.Prove([]byte("foo"))
root := tree.Root() // We also need the current tree root for the proof

// Verify the Merkle proof for foo=bar
if smt.VerifyProof(proof, root, []byte("foo"), []byte("bar"), sha256.New()) {
if smt.VerifyProof(proof, root, []byte("foo"), []byte("bar"), tree.Spec()) {
fmt.Println("Proof verification succeeded.")
} else {
fmt.Println("Proof verification failed.")
Expand Down
8 changes: 4 additions & 4 deletions bulk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ func bulkCheckAll(t *testing.T, smt *SMTWithStorage, kv []bulkop) {
if err != nil {
t.Errorf("error: %v", err)
}
if !VerifyProof(proof, smt.Root(), []byte(k), []byte(v), smt.base()) {
if !VerifyProof(proof, smt.Root(), []byte(k), []byte(v), smt.Spec()) {
t.Fatalf("Merkle proof failed to verify (i=%d): %v", ki, []byte(k))
}
compactProof, err := ProveCompact([]byte(k), smt)
if err != nil {
t.Errorf("error: %v", err)
}
if !VerifyCompactProof(compactProof, smt.Root(), []byte(k), []byte(v), smt.base()) {
if !VerifyCompactProof(compactProof, smt.Root(), []byte(k), []byte(v), smt.Spec()) {
t.Fatalf("Compact Merkle proof failed to verify (i=%d): %v", ki, []byte(k))
}

Expand All @@ -121,9 +121,9 @@ func bulkCheckAll(t *testing.T, smt *SMTWithStorage, kv []bulkop) {
continue
}

ph := smt.base().ph
ph := smt.Spec().ph
commonPrefix := countCommonPrefix(ph.Path([]byte(k)), ph.Path([]byte(k2)), 0)
if commonPrefix != smt.base().depth() && commonPrefix > largestCommonPrefix {
if commonPrefix != smt.Spec().depth() && commonPrefix > largestCommonPrefix {
largestCommonPrefix = commonPrefix
}
}
Expand Down
2 changes: 1 addition & 1 deletion fuzz/delete/fuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func Fuzz(data []byte) int {
}

smn := smt.NewSimpleMap()
tree := smt.NewSMT(smn, sha256.New())
tree := smt.NewSparseMerkleTree(smn, sha256.New())
for i := 0; i < len(splits)-1; i += 2 {
key, value := splits[i], splits[i+1]
tree.Update(key, value)
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func Fuzz(input []byte) int {
return 0
}
smn := smt.NewSimpleMap()
tree := smt.NewSMT(smn, sha256.New())
tree := smt.NewSparseMerkleTree(smn, sha256.New())
r := bytes.NewReader(input)
var keys [][]byte
key := func() []byte {
Expand Down
6 changes: 3 additions & 3 deletions hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ type PathHasher interface {

// ValueHasher defines how value data is hashed to produce leaf data.
type ValueHasher interface {
// hashValue hashes value data to produce the digest stored in leaf node.
hashValue([]byte) []byte
// HashValue hashes value data to produce the digest stored in leaf node.
HashValue([]byte) []byte
}

type treeHasher struct {
Expand Down Expand Up @@ -53,7 +53,7 @@ func (ph *pathHasher) PathSize() int {
return ph.hasher.Size()
}

func (vh *valueHasher) hashValue(data []byte) []byte {
func (vh *valueHasher) HashValue(data []byte) []byte {
return vh.digest(data)
}

Expand Down
4 changes: 2 additions & 2 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package smt
// Option is a function that configures SparseMerkleTree.
type Option func(*SMT)

func SetPathHasher(ph PathHasher) Option {
func WithPathHasher(ph PathHasher) Option {
return func(smt *SMT) { smt.ph = ph }
}

func SetValueHasher(vh ValueHasher) Option {
func WithValueHasher(vh ValueHasher) Option {
return func(smt *SMT) { smt.vh = vh }
}
60 changes: 30 additions & 30 deletions proofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ type SparseMerkleProof struct {
SiblingData []byte
}

func (proof *SparseMerkleProof) sanityCheck(base *BaseSMT) bool {
func (proof *SparseMerkleProof) sanityCheck(spec *TreeSpec) bool {
// Do a basic sanity check on the proof, so that a malicious proof cannot
// cause the verifier to fatally exit (e.g. due to an index out-of-range
// error) or cause a CPU DoS attack.

// Check that the number of supplied sidenodes does not exceed the maximum possible.
if len(proof.SideNodes) > base.ph.PathSize()*8 ||
if len(proof.SideNodes) > spec.ph.PathSize()*8 ||

// Check that leaf data for non-membership proofs is a valid size.
(proof.NonMembershipLeafData != nil && len(proof.NonMembershipLeafData) < len(leafPrefix)+base.ph.PathSize()) {
(proof.NonMembershipLeafData != nil && len(proof.NonMembershipLeafData) < len(leafPrefix)+spec.ph.PathSize()) {
return false
}

// Check that all supplied sidenodes are the correct size.
for _, v := range proof.SideNodes {
if len(v) != base.th.hashSize() {
if len(v) != spec.th.hashSize() {
return false
}
}
Expand All @@ -49,7 +49,7 @@ func (proof *SparseMerkleProof) sanityCheck(base *BaseSMT) bool {
return true
}

siblingHash := hashSerialization(base, proof.SiblingData)
siblingHash := hashSerialization(spec, proof.SiblingData)
return bytes.Equal(proof.SideNodes[0], siblingHash)
}

Expand Down Expand Up @@ -77,15 +77,15 @@ type SparseCompactMerkleProof struct {
SiblingData []byte
}

func (proof *SparseCompactMerkleProof) sanityCheck(base *BaseSMT) bool {
func (proof *SparseCompactMerkleProof) sanityCheck(spec *TreeSpec) bool {
// Do a basic sanity check on the proof on the fields of the proof specific to
// the compact proof only.
//
// When the proof is de-compacted and verified, the sanity check for the
// de-compacted proof should be executed.

// Compact proofs: check that NumSideNodes is within the right range.
if proof.NumSideNodes < 0 || proof.NumSideNodes > base.ph.PathSize()*8 ||
if proof.NumSideNodes < 0 || proof.NumSideNodes > spec.ph.PathSize()*8 ||

// Compact proofs: check that the length of the bit mask is as expected
// according to NumSideNodes.
Expand All @@ -101,15 +101,15 @@ func (proof *SparseCompactMerkleProof) sanityCheck(base *BaseSMT) bool {
}

// VerifyProof verifies a Merkle proof.
func VerifyProof(proof SparseMerkleProof, root []byte, key []byte, value []byte, base *BaseSMT) bool {
result, _ := verifyProofWithUpdates(proof, root, key, value, base)
func VerifyProof(proof SparseMerkleProof, root []byte, key []byte, value []byte, spec *TreeSpec) bool {
result, _ := verifyProofWithUpdates(proof, root, key, value, spec)
return result
}

func verifyProofWithUpdates(proof SparseMerkleProof, root []byte, key []byte, value []byte, base *BaseSMT) (bool, [][][]byte) {
path := base.ph.Path(key)
func verifyProofWithUpdates(proof SparseMerkleProof, root []byte, key []byte, value []byte, spec *TreeSpec) (bool, [][][]byte) {
path := spec.ph.Path(key)

if !proof.sanityCheck(base) {
if !proof.sanityCheck(spec) {
return false, nil
}

Expand All @@ -119,36 +119,36 @@ func verifyProofWithUpdates(proof SparseMerkleProof, root []byte, key []byte, va
var currentHash, currentData []byte
if bytes.Equal(value, defaultValue) { // Non-membership proof.
if proof.NonMembershipLeafData == nil { // Leaf is a placeholder value.
currentHash = base.th.placeholder()
currentHash = spec.th.placeholder()
} else { // Leaf is an unrelated leaf.
actualPath, valueHash := parseLeaf(proof.NonMembershipLeafData, base.ph)
actualPath, valueHash := parseLeaf(proof.NonMembershipLeafData, spec.ph)
if bytes.Equal(actualPath, path) {
// This is not an unrelated leaf; non-membership proof failed.
return false, nil
}
currentHash, currentData = base.th.digestLeaf(actualPath, valueHash)
currentHash, currentData = spec.th.digestLeaf(actualPath, valueHash)

update := make([][]byte, 2)
update[0], update[1] = currentHash, currentData
updates = append(updates, update)
}
} else { // Membership proof.
valueHash := base.digestValue(value)
currentHash, currentData = base.th.digestLeaf(path, valueHash)
valueHash := spec.digestValue(value)
currentHash, currentData = spec.th.digestLeaf(path, valueHash)
update := make([][]byte, 2)
update[0], update[1] = currentHash, currentData
updates = append(updates, update)
}

// Recompute root.
for i := 0; i < len(proof.SideNodes); i++ {
node := make([]byte, base.th.hashSize())
node := make([]byte, spec.th.hashSize())
copy(node, proof.SideNodes[i])

if getPathBit(path, len(proof.SideNodes)-1-i) == left {
currentHash, currentData = base.th.digestNode(currentHash, node)
currentHash, currentData = spec.th.digestNode(currentHash, node)
} else {
currentHash, currentData = base.th.digestNode(node, currentHash)
currentHash, currentData = spec.th.digestNode(node, currentHash)
}

update := make([][]byte, 2)
Expand All @@ -160,26 +160,26 @@ func verifyProofWithUpdates(proof SparseMerkleProof, root []byte, key []byte, va
}

// VerifyCompactProof verifies a compacted Merkle proof.
func VerifyCompactProof(proof SparseCompactMerkleProof, root []byte, key, value []byte, base *BaseSMT) bool {
decompactedProof, err := DecompactProof(proof, base)
func VerifyCompactProof(proof SparseCompactMerkleProof, root []byte, key, value []byte, spec *TreeSpec) bool {
decompactedProof, err := DecompactProof(proof, spec)
if err != nil {
return false
}
return VerifyProof(decompactedProof, root, key, value, base)
return VerifyProof(decompactedProof, root, key, value, spec)
}

// CompactProof compacts a proof, to reduce its size.
func CompactProof(proof SparseMerkleProof, base *BaseSMT) (SparseCompactMerkleProof, error) {
if !proof.sanityCheck(base) {
func CompactProof(proof SparseMerkleProof, spec *TreeSpec) (SparseCompactMerkleProof, error) {
if !proof.sanityCheck(spec) {
return SparseCompactMerkleProof{}, ErrBadProof
}

bitMask := make([]byte, int(math.Ceil(float64(len(proof.SideNodes))/float64(8))))
var compactedSideNodes [][]byte
for i := 0; i < len(proof.SideNodes); i++ {
node := make([]byte, base.th.hashSize())
node := make([]byte, spec.th.hashSize())
copy(node, proof.SideNodes[i])
if bytes.Equal(node, base.th.placeholder()) {
if bytes.Equal(node, spec.th.placeholder()) {
setPathBit(bitMask, i)
} else {
compactedSideNodes = append(compactedSideNodes, node)
Expand All @@ -196,16 +196,16 @@ func CompactProof(proof SparseMerkleProof, base *BaseSMT) (SparseCompactMerklePr
}

// DecompactProof decompacts a proof, so that it can be used for VerifyProof.
func DecompactProof(proof SparseCompactMerkleProof, base *BaseSMT) (SparseMerkleProof, error) {
if !proof.sanityCheck(base) {
func DecompactProof(proof SparseCompactMerkleProof, spec *TreeSpec) (SparseMerkleProof, error) {
if !proof.sanityCheck(spec) {
return SparseMerkleProof{}, ErrBadProof
}

decompactedSideNodes := make([][]byte, proof.NumSideNodes)
position := 0
for i := 0; i < proof.NumSideNodes; i++ {
if getPathBit(proof.BitMask, i) == 1 {
decompactedSideNodes[i] = base.th.placeholder()
decompactedSideNodes[i] = spec.th.placeholder()
} else {
decompactedSideNodes[i] = proof.SideNodes[position]
position++
Expand Down
2 changes: 1 addition & 1 deletion proofs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func randomiseProof(proof SparseMerkleProof) SparseMerkleProof {
}

// Check that a non-compact proof is equivalent to the proof returned when it is compacted and de-compacted.
func checkCompactEquivalence(t *testing.T, proof SparseMerkleProof, base *BaseSMT) {
func checkCompactEquivalence(t *testing.T, proof SparseMerkleProof, base *TreeSpec) {
t.Helper()
compactedProof, err := CompactProof(proof, base)
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions smt.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type leafNode struct {
// A compressed chain of singly-linked inner nodes
type extensionNode struct {
path []byte
// Bit offsets into path slice defining actual path segment.
// Offsets into path slice of bounds defining actual path segment.
// Note: assumes path is <=256 bits
pathBounds [2]byte
// Child is always an inner node, or lazy.
Expand All @@ -49,7 +49,7 @@ type lazyNode struct {
}

type SMT struct {
BaseSMT
TreeSpec
nodes MapStore
// Last persisted root hash
savedRoot []byte
Expand All @@ -62,19 +62,19 @@ type SMT struct {
// Hashes of persisted nodes deleted from tree
type orphanNodes = [][]byte

func NewSMT(nodes MapStore, hasher hash.Hash, options ...Option) *SMT {
func NewSparseMerkleTree(nodes MapStore, hasher hash.Hash, options ...Option) *SMT {
smt := SMT{
BaseSMT: newBaseSMT(hasher),
nodes: nodes,
TreeSpec: newTreeSpec(hasher),
nodes: nodes,
}
for _, option := range options {
option(&smt)
}
return &smt
}

func ImportSMT(nodes MapStore, hasher hash.Hash, root []byte, options ...Option) *SMT {
smt := NewSMT(nodes, hasher, options...)
func ImportSparseMerkleTree(nodes MapStore, hasher hash.Hash, root []byte, options ...Option) *SMT {
smt := NewSparseMerkleTree(nodes, hasher, options...)
smt.tree = &lazyNode{root}
smt.savedRoot = root
return smt
Expand Down
6 changes: 3 additions & 3 deletions smt_proofs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestProofsBasic(t *testing.T) {

smn, smv = NewSimpleMap(), NewSimpleMap()
smt = NewSMTWithStorage(smn, smv, sha256.New())
base := smt.base()
base := smt.Spec()

// Generate and verify a proof on an empty key.
proof, err = smt.Prove([]byte("testKey3"))
Expand Down Expand Up @@ -90,7 +90,7 @@ func TestProofsBasic(t *testing.T) {
func TestProofsSanityCheck(t *testing.T) {
smn, smv := NewSimpleMap(), NewSimpleMap()
smt := NewSMTWithStorage(smn, smv, sha256.New())
base := smt.base()
base := smt.Spec()

smt.Update([]byte("testKey1"), []byte("testValue1"))
smt.Update([]byte("testKey2"), []byte("testValue2"))
Expand All @@ -100,7 +100,7 @@ func TestProofsSanityCheck(t *testing.T) {

// Case: invalid number of sidenodes.
proof, _ := smt.Prove([]byte("testKey1"))
sideNodes := make([][]byte, smt.base().depth()+1)
sideNodes := make([][]byte, smt.Spec().depth()+1)
for i := range sideNodes {
sideNodes[i] = proof.SideNodes[0]
}
Expand Down
Loading

0 comments on commit 676615b

Please sign in to comment.