Skip to content

Commit 1da9b95

Browse files
committed
mssmt: add InsertMany method to full and compacted trees
This commit introduces the InsertMany method to both the FullTree and CompactedTree implementations of the MS-SMT. This method allows for the insertion of multiple leaf nodes in a single database transaction, improving efficiency when adding multiple leaves at once. The InsertMany method is added to the Tree interface and implemented in both FullTree and CompactedTree. The implementation includes sum overflow checks before each insertion and updates the root within the transaction for consistency. A new test case, TestInsertMany, is added to verify the functionality of the InsertMany method in both FullTree and CompactedTree. The test inserts a random set of leaves using InsertMany and verifies the resulting root and retrieved leaves. The Copy method in both FullTree and CompactedTree is updated to use InsertMany for efficiency when copying leaves to the target tree.
1 parent b523e33 commit 1da9b95

File tree

4 files changed

+279
-32
lines changed

4 files changed

+279
-32
lines changed

mssmt/compacted_tree.go

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,15 +510,73 @@ func (t *CompactedTree) Copy(ctx context.Context, targetTree Tree) error {
510510
return err
511511
}
512512

513-
// Insert all found leaves into the target tree.
514-
for key, leaf := range leaves {
515-
// Use the target tree's Insert method.
516-
_, err := targetTree.Insert(ctx, key, leaf)
513+
// Insert all found leaves into the target tree using InsertMany for
514+
// efficiency.
515+
_, err = targetTree.InsertMany(ctx, leaves)
516+
if err != nil {
517+
return fmt.Errorf("error inserting leaves into "+
518+
"target tree: %w", err)
519+
}
520+
521+
return nil
522+
}
523+
524+
// InsertMany inserts multiple leaf nodes provided in the leaves map within a
525+
// single database transaction.
526+
func (t *CompactedTree) InsertMany(ctx context.Context,
527+
leaves map[[hashSize]byte]*LeafNode) (Tree, error) {
528+
529+
if len(leaves) == 0 {
530+
return t, nil
531+
}
532+
533+
dbErr := t.store.Update(ctx, func(tx TreeStoreUpdateTx) error {
534+
currentRoot, err := tx.RootNode()
517535
if err != nil {
518-
return fmt.Errorf("error inserting leaf with key %x "+
519-
"into target tree: %w", key, err)
536+
return err
520537
}
538+
rootBranch := currentRoot.(*BranchNode)
539+
540+
for key, leaf := range leaves {
541+
// Check for potential sum overflow before each
542+
// insertion.
543+
sumRoot := rootBranch.NodeSum()
544+
sumLeaf := leaf.NodeSum()
545+
err = CheckSumOverflowUint64(sumRoot, sumLeaf)
546+
if err != nil {
547+
return fmt.Errorf("compact tree leaf insert "+
548+
"sum overflow, root: %d, leaf: %d; %w",
549+
sumRoot, sumLeaf, err)
550+
}
551+
552+
// Insert the leaf using the internal helper.
553+
newRoot, err := t.insert(
554+
tx, &key, 0, rootBranch, leaf,
555+
)
556+
if err != nil {
557+
return fmt.Errorf("error inserting leaf "+
558+
"with key %x: %w", key, err)
559+
}
560+
rootBranch = newRoot
561+
562+
// Update the root within the transaction for
563+
// consistency, even though the insert logic passes the
564+
// root explicitly.
565+
err = tx.UpdateRoot(rootBranch)
566+
if err != nil {
567+
return fmt.Errorf("error updating root "+
568+
"during InsertMany: %w", err)
569+
}
570+
}
571+
572+
// The root is already updated by the last iteration of the
573+
// loop. No final update needed here, but returning nil error
574+
// signals success.
575+
return nil
576+
})
577+
if dbErr != nil {
578+
return nil, dbErr
521579
}
522580

523-
return nil
581+
return t, nil
524582
}

mssmt/interface.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ type Tree interface {
3131
// leaf.
3232
MerkleProof(ctx context.Context, key [hashSize]byte) (*Proof, error)
3333

34+
// InsertMany inserts multiple leaf nodes provided in the leaves map
35+
// within a single database transaction.
36+
InsertMany(ctx context.Context, leaves map[[hashSize]byte]*LeafNode) (
37+
Tree, error)
38+
3439
// Copy copies all the key-value pairs from the source tree into the
3540
// target tree.
3641
Copy(ctx context.Context, targetTree Tree) error

mssmt/tree.go

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,8 @@ func (t *FullTree) MerkleProof(ctx context.Context, key [hashSize]byte) (
336336
// findLeaves recursively traverses the tree represented by the given node and
337337
// collects all non-empty leaf nodes along with their reconstructed keys.
338338
func findLeaves(ctx context.Context, tx TreeStoreViewTx, node Node,
339-
keyPrefix [hashSize]byte, depth int) (map[[hashSize]byte]*LeafNode, error) {
339+
keyPrefix [hashSize]byte,
340+
depth int) (map[[hashSize]byte]*LeafNode, error) {
340341

341342
// Base case: If it's a leaf node.
342343
if leafNode, ok := node.(*LeafNode); ok {
@@ -423,20 +424,72 @@ func (t *FullTree) Copy(ctx context.Context, targetTree Tree) error {
423424
return err
424425
}
425426

426-
// Insert all found leaves into the target tree. We assume the target
427-
// tree handles batching or individual inserts efficiently.
428-
for key, leaf := range leaves {
429-
// Use the target tree's Insert method. We ignore the returned
430-
// tree as we are modifying the targetTree in place via its
431-
// store.
432-
_, err := targetTree.Insert(ctx, key, leaf)
427+
// Insert all found leaves into the target tree using InsertMany for
428+
// efficiency.
429+
_, err = targetTree.InsertMany(ctx, leaves)
430+
if err != nil {
431+
return fmt.Errorf("error inserting leaves into target "+
432+
"tree: %w", err)
433+
}
434+
435+
return nil
436+
}
437+
438+
// InsertMany inserts multiple leaf nodes provided in the leaves map within a
439+
// single database transaction.
440+
func (t *FullTree) InsertMany(ctx context.Context,
441+
leaves map[[hashSize]byte]*LeafNode) (Tree, error) {
442+
443+
if len(leaves) == 0 {
444+
return t, nil
445+
}
446+
447+
err := t.store.Update(ctx, func(tx TreeStoreUpdateTx) error {
448+
currentRoot, err := tx.RootNode()
433449
if err != nil {
434-
return fmt.Errorf("error inserting leaf with key %x "+
435-
"into target tree: %w", key, err)
450+
return err
436451
}
452+
rootBranch := currentRoot.(*BranchNode)
453+
454+
for key, leaf := range leaves {
455+
// Check for potential sum overflow before each
456+
// insertion.
457+
sumRoot := rootBranch.NodeSum()
458+
sumLeaf := leaf.NodeSum()
459+
err = CheckSumOverflowUint64(sumRoot, sumLeaf)
460+
if err != nil {
461+
return fmt.Errorf("full tree leaf insert sum "+
462+
"overflow, root: %d, leaf: %d; %w",
463+
sumRoot, sumLeaf, err)
464+
}
465+
466+
// Insert the leaf using the internal helper.
467+
newRoot, err := t.insert(tx, &key, leaf)
468+
if err != nil {
469+
return fmt.Errorf("error inserting leaf "+
470+
"with key %x: %w", key, err)
471+
}
472+
rootBranch = newRoot
473+
474+
// Update the root within the transaction so subsequent
475+
// inserts in this batch read the correct state.
476+
err = tx.UpdateRoot(rootBranch)
477+
if err != nil {
478+
return fmt.Errorf("error updating root "+
479+
"during InsertMany: %w", err)
480+
}
481+
}
482+
483+
// The root is already updated by the last iteration of the
484+
// loop. No final update needed here, but returning nil error
485+
// signals success.
486+
return nil
487+
})
488+
if err != nil {
489+
return nil, err
437490
}
438491

439-
return nil
492+
return t, nil
440493
}
441494

442495
// VerifyMerkleProof determines whether a merkle proof for the leaf found at the

0 commit comments

Comments
 (0)