Skip to content

Commit

Permalink
Merge pull request #91 from hyperledger-labs/index-vs-ref
Browse files Browse the repository at this point in the history
Split node reference vs. index to separate interfaces for API clarity
  • Loading branch information
jimthematrix authored Oct 4, 2024
2 parents b463b4a + 7696343 commit aa3c1fd
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 94 deletions.
4 changes: 2 additions & 2 deletions go-sdk/integration-test/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (s *SqliteTestSuite) TestSqliteStorage() {
dbRoot := core.SMTRoot{Name: s.smtName}
err = s.gormDB.Table(core.TreeRootsTable).First(&dbRoot).Error
assert.NoError(s.T(), err)
assert.Equal(s.T(), root.Hex(), dbRoot.RootIndex)
assert.Equal(s.T(), root.Hex(), dbRoot.RootRef)

dbNode := core.SMTNode{RefKey: n1.Ref().Hex()}
err = s.gormDB.Table(core.NodesTablePrefix + s.smtName).First(&dbNode).Error
Expand Down Expand Up @@ -155,7 +155,7 @@ func TestPostgresStorage(t *testing.T) {
dbRoot := core.SMTRoot{Name: "test_1"}
err = db.Table(core.TreeRootsTable).First(&dbRoot).Error
assert.NoError(t, err)
assert.Equal(t, root.Hex(), dbRoot.RootIndex)
assert.Equal(t, root.Hex(), dbRoot.RootRef)

dbNode := core.SMTNode{RefKey: n1.Ref().Hex()}
err = db.Table(core.NodesTablePrefix + "test_1").First(&dbNode).Error
Expand Down
20 changes: 10 additions & 10 deletions go-sdk/internal/sparse-merkle-tree/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ type nodeIndex [32]byte
type node struct {
nodeType core.NodeType
index core.NodeIndex
refKey core.NodeIndex
refKey core.NodeRef
value core.Indexable
leftChild core.NodeIndex
rightChild core.NodeIndex
leftChild core.NodeRef
rightChild core.NodeRef
}

// //////////////////////////////////////
Expand Down Expand Up @@ -70,7 +70,7 @@ func NewLeafNode(v core.Indexable) (core.Node, error) {
return n, err
}

func NewBranchNode(leftChild, rightChild core.NodeIndex) (core.Node, error) {
func NewBranchNode(leftChild, rightChild core.NodeRef) (core.Node, error) {
n := &node{nodeType: core.NodeTypeBranch, leftChild: leftChild, rightChild: rightChild}
elements := []*big.Int{leftChild.BigInt(), rightChild.BigInt()}
hash, err := poseidon.Hash(elements)
Expand All @@ -89,19 +89,19 @@ func (n *node) Index() core.NodeIndex {
return n.index
}

func (n *node) Ref() core.NodeIndex {
func (n *node) Ref() core.NodeRef {
return n.refKey
}

func (n *node) Value() core.Indexable {
return n.value
}

func (n *node) LeftChild() core.NodeIndex {
func (n *node) LeftChild() core.NodeRef {
return n.leftChild
}

func (n *node) RightChild() core.NodeIndex {
func (n *node) RightChild() core.NodeRef {
return n.rightChild
}

Expand Down Expand Up @@ -145,20 +145,20 @@ func (idx *nodeIndex) IsZero() bool {
return idx.BigInt().Sign() == 0
}

func (idx *nodeIndex) Equal(other core.NodeIndex) bool {
func (idx *nodeIndex) Equal(other core.NodeRef) bool {
return idx.BigInt().Cmp(other.BigInt()) == 0
}

// getPath returns the binary path, from the root to the leaf.
func (idx *nodeIndex) ToPath(levels int) []bool {
path := make([]bool, levels)
for l := 0; l < levels; l++ {
path[l] = idx.IsBitOn(uint(l))
path[l] = idx.IsBitOne(uint(l))
}
return path
}

func (idx *nodeIndex) IsBitOn(pos uint) bool {
func (idx *nodeIndex) IsBitOne(pos uint) bool {
if pos >= 256 {
return false
}
Expand Down
30 changes: 15 additions & 15 deletions go-sdk/internal/sparse-merkle-tree/smt/merkletree.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const MAX_TREE_HEIGHT = 256
type sparseMerkleTree struct {
sync.RWMutex
db core.Storage
rootKey core.NodeIndex
rootKey core.NodeRef
maxLevels int
}

Expand All @@ -44,10 +44,10 @@ func NewMerkleTree(db core.Storage, maxLevels int) (core.SparseMerkleTree, error
}
mt := sparseMerkleTree{db: db, maxLevels: maxLevels}

root, err := mt.db.GetRootNodeIndex()
root, err := mt.db.GetRootNodeRef()
if err == core.ErrNotFound {
mt.rootKey = node.ZERO_INDEX
err = mt.db.UpsertRootNodeIndex(mt.rootKey)
err = mt.db.UpsertRootNodeRef(mt.rootKey)
if err != nil {
return nil, err
}
Expand All @@ -59,7 +59,7 @@ func NewMerkleTree(db core.Storage, maxLevels int) (core.SparseMerkleTree, error
return &mt, nil
}

func (mt *sparseMerkleTree) Root() core.NodeIndex {
func (mt *sparseMerkleTree) Root() core.NodeRef {
mt.RLock()
defer mt.RUnlock()
return mt.rootKey
Expand Down Expand Up @@ -93,7 +93,7 @@ func (mt *sparseMerkleTree) AddLeaf(node core.Node) error {

// update the root node index in the storage
log.L().Infof("Upserting root node index to %s", mt.rootKey.Hex())
err = batch.UpsertRootNodeIndex(mt.rootKey)
err = batch.UpsertRootNodeRef(mt.rootKey)
if err != nil {
log.L().Errorf("Error upserting root node %s: %v, rolling back", mt.rootKey.Hex(), err)
_ = batch.Rollback()
Expand All @@ -114,7 +114,7 @@ func (mt *sparseMerkleTree) AddLeaf(node core.Node) error {

// GetNode gets a node by key from the merkle tree. Empty nodes are not stored in the
// tree: they are all the same and assumed to always exist.
func (mt *sparseMerkleTree) GetNode(key core.NodeIndex) (core.Node, error) {
func (mt *sparseMerkleTree) GetNode(key core.NodeRef) (core.Node, error) {
mt.RLock()
defer mt.RUnlock()
return mt.getNode(key)
Expand All @@ -123,7 +123,7 @@ func (mt *sparseMerkleTree) GetNode(key core.NodeIndex) (core.Node, error) {
// GenerateProofs generates a list of proofs of existence (or non-existence) of the provided
// leaf nodes that are represented by their indexes. An optional Merkle tree root can be provided.
// If rootKey is not provided, the current Merkle tree root is used
func (mt *sparseMerkleTree) GenerateProofs(keys []*big.Int, rootKey core.NodeIndex) ([]core.Proof, []*big.Int, error) {
func (mt *sparseMerkleTree) GenerateProofs(keys []*big.Int, rootKey core.NodeRef) ([]core.Proof, []*big.Int, error) {
mt.RLock()
defer mt.RUnlock()

Expand All @@ -141,9 +141,9 @@ func (mt *sparseMerkleTree) GenerateProofs(keys []*big.Int, rootKey core.NodeInd
return merkleProofs, foundValues, nil
}

func (mt *sparseMerkleTree) generateProof(key *big.Int, rootKey core.NodeIndex) (core.Proof, *big.Int, error) {
func (mt *sparseMerkleTree) generateProof(key *big.Int, rootKey core.NodeRef) (core.Proof, *big.Int, error) {
p := &proof{}
var siblingKey core.NodeIndex
var siblingKey core.NodeRef

kHash, err := node.NewNodeIndexFromBigInt(key)
if err != nil {
Expand Down Expand Up @@ -198,7 +198,7 @@ func (mt *sparseMerkleTree) generateProof(key *big.Int, rootKey core.NodeIndex)
}

// must be called from inside a read lock
func (mt *sparseMerkleTree) getNode(key core.NodeIndex) (core.Node, error) {
func (mt *sparseMerkleTree) getNode(key core.NodeRef) (core.Node, error) {
if key.IsZero() {
return node.NewEmptyNode(), nil
}
Expand All @@ -219,7 +219,7 @@ func (mt *sparseMerkleTree) getNode(key core.NodeIndex) (core.Node, error) {
// as children of a new branch node.
// - if the current node is a branch node, it will continue traversing the tree, using the
// next bit of the new node's index to determine which child to go down to.
func (mt *sparseMerkleTree) addLeaf(batch core.Transaction, newLeaf core.Node, currentNodeIndex core.NodeIndex, level int, path []bool) (core.NodeIndex, error) {
func (mt *sparseMerkleTree) addLeaf(batch core.Transaction, newLeaf core.Node, currentNodeRef core.NodeRef, level int, path []bool) (core.NodeRef, error) {
log.WithLogField("level", strconv.Itoa(level)).Debugf("Adding leaf node %s", newLeaf.Ref().Hex())
if level > mt.maxLevels-1 {
// we have exhausted all levels but could not find a unique path for the new leaf.
Expand All @@ -228,8 +228,8 @@ func (mt *sparseMerkleTree) addLeaf(batch core.Transaction, newLeaf core.Node, c
return nil, ErrReachedMaxLevel
}

var nextKey core.NodeIndex
currentNode, err := mt.getNode(currentNodeIndex)
var nextKey core.NodeRef
currentNode, err := mt.getNode(currentNodeRef)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -289,7 +289,7 @@ func (mt *sparseMerkleTree) addLeaf(batch core.Transaction, newLeaf core.Node, c
// must be called from inside a write lock
// addNode adds a node into the MT. Empty nodes are not stored in the tree;
// they are all the same and assumed to always exist.
func (mt *sparseMerkleTree) addNode(batch core.Transaction, n core.Node) (core.NodeIndex, error) {
func (mt *sparseMerkleTree) addNode(batch core.Transaction, n core.Node) (core.NodeRef, error) {
if n.Type() == core.NodeTypeEmpty {
return n.Ref(), nil
}
Expand All @@ -301,7 +301,7 @@ func (mt *sparseMerkleTree) addNode(batch core.Transaction, n core.Node) (core.N
// must be called from inside a write lock
// extendPath extends the path of two leaf nodes, which share the same beginnging part of
// their indexes, until their paths diverge, creating ancestor branch nodes as needed.
func (mt *sparseMerkleTree) extendPath(batch core.Transaction, newLeaf core.Node, oldLeaf core.Node, level int, pathNewLeaf []bool, pathOldLeaf []bool) (core.NodeIndex, error) {
func (mt *sparseMerkleTree) extendPath(batch core.Transaction, newLeaf core.Node, oldLeaf core.Node, level int, pathNewLeaf []bool, pathOldLeaf []bool) (core.NodeRef, error) {
if level > mt.maxLevels-2 {
return nil, ErrReachedMaxLevel
}
Expand Down
6 changes: 3 additions & 3 deletions go-sdk/internal/sparse-merkle-tree/smt/merkletree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ type mockStorage struct {
GetRootNodeIndex_customError bool
}

func (ms *mockStorage) GetRootNodeIndex() (core.NodeIndex, error) {
func (ms *mockStorage) GetRootNodeRef() (core.NodeRef, error) {
if ms.GetRootNodeIndex_customError {
return nil, fmt.Errorf("nasty error in get root")
}
return nil, core.ErrNotFound
}
func (ms *mockStorage) UpsertRootNodeIndex(core.NodeIndex) error {
func (ms *mockStorage) UpsertRootNodeRef(core.NodeRef) error {
return fmt.Errorf("nasty error in upsert root")
}
func (ms *mockStorage) GetNode(core.NodeIndex) (core.Node, error) {
func (ms *mockStorage) GetNode(core.NodeRef) (core.Node, error) {
return nil, nil
}
func (ms *mockStorage) InsertNode(core.Node) error {
Expand Down
20 changes: 10 additions & 10 deletions go-sdk/internal/sparse-merkle-tree/smt/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
// non-existence.
type proof struct {
existence bool
siblings []core.NodeIndex
siblings []core.NodeRef
depth uint
existingNode core.Node
// nonEmptySiblings is a bitmap of non-empty Siblings found in Siblings. This helps
Expand All @@ -40,7 +40,7 @@ func (p *proof) IsExistenceProof() bool {
return p.existence
}

func (p *proof) Siblings() []core.NodeIndex {
func (p *proof) Siblings() []core.NodeRef {
return p.siblings
}

Expand Down Expand Up @@ -71,9 +71,9 @@ func (p *proof) IsNonEmptySibling(level uint) bool {
return isBitOnBigEndian(p.nonEmptySiblings, level)
}

func (p *proof) AllSiblings() []core.NodeIndex {
func (p *proof) AllSiblings() []core.NodeRef {
sibIdx := 0
siblings := []core.NodeIndex{}
siblings := []core.NodeRef{}
for level := 0; level < int(p.depth); level++ {
if p.IsNonEmptySibling(uint(level)) {
siblings = append(siblings, p.siblings[sibIdx])
Expand All @@ -89,15 +89,15 @@ func (p *proof) AllSiblings() []core.NodeIndex {
func (p *proof) getPath(index core.NodeIndex) []bool {
path := make([]bool, p.depth)
for n := 0; n < int(p.depth); n++ {
path[n] = index.IsBitOn(uint(n))
path[n] = index.IsBitOne(uint(n))
}
return path
}

// ToCircomVerifierProof enhances the generic merkle proof with additional
// signals required by the circuit for Sparse Merkle Tree proof verification:
// https://github.com/iden3/circomlib/blob/master/circuits/smt/smtverifier.circom
func (p *proof) ToCircomVerifierProof(k, v *big.Int, rootKey core.NodeIndex, levels int) (*core.CircomVerifierProof, error) {
func (p *proof) ToCircomVerifierProof(k, v *big.Int, rootKey core.NodeRef, levels int) (*core.CircomVerifierProof, error) {
var cp core.CircomVerifierProof
cp.Root = rootKey
cp.Siblings = p.AllSiblings()
Expand Down Expand Up @@ -131,7 +131,7 @@ func (p *proof) ToCircomVerifierProof(k, v *big.Int, rootKey core.NodeIndex, lev
}

// VerifyProof verifies the Merkle Proof for the entry and root.
func VerifyProof(rootKey core.NodeIndex, p core.Proof, leafNode core.Node) bool {
func VerifyProof(rootKey core.NodeRef, p core.Proof, leafNode core.Node) bool {
rootFromProof, err := calculateRootFromProof(p.(*proof), leafNode)
if err != nil {
return false
Expand All @@ -141,9 +141,9 @@ func VerifyProof(rootKey core.NodeIndex, p core.Proof, leafNode core.Node) bool

// CalculateRootFromProof calculates the root that would correspond to a tree whose
// siblings are the ones in the proof with the leaf node
func calculateRootFromProof(proof *proof, leafNode core.Node) (core.NodeIndex, error) {
func calculateRootFromProof(proof *proof, leafNode core.Node) (core.NodeRef, error) {
sibIdx := len(proof.siblings) - 1
var midKey core.NodeIndex
var midKey core.NodeRef
if proof.existence {
midKey = leafNode.Ref()
} else {
Expand All @@ -157,7 +157,7 @@ func calculateRootFromProof(proof *proof, leafNode core.Node) (core.NodeIndex, e
}
}
path := proof.getPath(leafNode.Index())
var siblingKey core.NodeIndex
var siblingKey core.NodeRef
for level := int(proof.depth) - 1; level >= 0; level-- {
if proof.IsNonEmptySibling(uint(level)) {
siblingKey = proof.siblings[sibIdx]
Expand Down
6 changes: 4 additions & 2 deletions go-sdk/internal/sparse-merkle-tree/smt/smt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,12 @@ func (s *MerkleTreeTestSuite) TestAddNode() {

// test storage persistence
rawDB := mt.(*sparseMerkleTree).db
rootIdx, err := rawDB.GetRootNodeIndex()
rootIdx, err := rawDB.GetRootNodeRef()
assert.NoError(s.T(), err)
assert.Equal(s.T(), "abacf46f5217552ee28fe50b8fd7ca6aa46daeb9acf9f60928654c3b1a472f23", rootIdx.Hex())
dbNode1, err := rawDB.GetNode(n1.Ref())
assert.NoError(s.T(), err)
assert.Equal(s.T(), n1.Index().Hex(), dbNode1.Index().Hex())

// test storage persistence across tree creation
mt2, err := NewMerkleTree(s.db, 10)
Expand Down Expand Up @@ -242,7 +245,6 @@ func (s *MerkleTreeTestSuite) TestVerifyProof() {
assert.NoError(s.T(), err)
startProving <- node
done <- true
fmt.Printf("Added node %d\n", idx)
}(value, idx)
}

Expand Down
24 changes: 12 additions & 12 deletions go-sdk/internal/sparse-merkle-tree/storage/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func NewSqlStorage(p core.SqlDBProvider, smtName string) *sqlStorage {
}
}

func (s *sqlStorage) GetRootNodeIndex() (core.NodeIndex, error) {
func (s *sqlStorage) GetRootNodeRef() (core.NodeRef, error) {
root := core.SMTRoot{
Name: s.smtName,
}
Expand All @@ -51,15 +51,15 @@ func (s *sqlStorage) GetRootNodeIndex() (core.NodeIndex, error) {
} else if err != nil {
return nil, err
}
idx, err := node.NewNodeIndexFromHex(root.RootIndex)
idx, err := node.NewNodeIndexFromHex(root.RootRef)
return idx, err
}

func (s *sqlStorage) UpsertRootNodeIndex(root core.NodeIndex) error {
return upsertRootNodeIndex(s.p.DB(), s.smtName, root)
func (s *sqlStorage) UpsertRootNodeRef(root core.NodeRef) error {
return upsertRootNodeRef(s.p.DB(), s.smtName, root)
}

func (s *sqlStorage) GetNode(ref core.NodeIndex) (core.Node, error) {
func (s *sqlStorage) GetNode(ref core.NodeRef) (core.Node, error) {
return getNode(s.p.DB(), s.nodesTableName, ref)
}

Expand All @@ -81,11 +81,11 @@ type sqlTxStorage struct {
nodesTableName string
}

func (b *sqlTxStorage) UpsertRootNodeIndex(root core.NodeIndex) error {
return upsertRootNodeIndex(b.tx, b.smtName, root)
func (b *sqlTxStorage) UpsertRootNodeRef(root core.NodeRef) error {
return upsertRootNodeRef(b.tx, b.smtName, root)
}

func (b *sqlTxStorage) GetNode(ref core.NodeIndex) (core.Node, error) {
func (b *sqlTxStorage) GetNode(ref core.NodeRef) (core.Node, error) {
return getNode(b.tx, b.nodesTableName, ref)
}

Expand All @@ -105,15 +105,15 @@ func (m *sqlStorage) Close() {
m.p.Close()
}

func upsertRootNodeIndex(batchOrDb *gorm.DB, name string, root core.NodeIndex) error {
func upsertRootNodeRef(batchOrDb *gorm.DB, name string, root core.NodeRef) error {
err := batchOrDb.Table(core.TreeRootsTable).Save(&core.SMTRoot{
RootIndex: root.Hex(),
Name: name,
RootRef: root.Hex(),
Name: name,
}).Error
return err
}

func getNode(batchOrDb *gorm.DB, nodesTableName string, ref core.NodeIndex) (core.Node, error) {
func getNode(batchOrDb *gorm.DB, nodesTableName string, ref core.NodeRef) (core.Node, error) {
// the node's reference key (not the index) is used as the key to
// store the node in the DB
n := core.SMTNode{
Expand Down
Loading

0 comments on commit aa3c1fd

Please sign in to comment.