Skip to content

Commit bd32d61

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 ddb410d commit bd32d61

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
@@ -344,7 +344,8 @@ func (t *FullTree) MerkleProof(ctx context.Context, key [hashSize]byte) (
344344
// findLeaves recursively traverses the tree represented by the given node and
345345
// collects all non-empty leaf nodes along with their reconstructed keys.
346346
func findLeaves(ctx context.Context, tx TreeStoreViewTx, node Node,
347-
keyPrefix [hashSize]byte, depth int) (map[[hashSize]byte]*LeafNode, error) {
347+
keyPrefix [hashSize]byte,
348+
depth int) (map[[hashSize]byte]*LeafNode, error) {
348349

349350
// Base case: If it's a leaf node.
350351
if leafNode, ok := node.(*LeafNode); ok {
@@ -428,20 +429,72 @@ func (t *FullTree) Copy(ctx context.Context, targetTree Tree) error {
428429
return err
429430
}
430431

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

444-
return nil
497+
return t, nil
445498
}
446499

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

0 commit comments

Comments
 (0)