Skip to content

Commit 7f90724

Browse files
jsigngballet
authored andcommitted
tree: allow batching new leaf nodes
Signed-off-by: Ignacio Hagopian <[email protected]> tree: simplify batching of new leaves creation Signed-off-by: Ignacio Hagopian <[email protected]> tree: fix insert new leaves test and benchmark Signed-off-by: Ignacio Hagopian <[email protected]> remove comment Signed-off-by: Ignacio Hagopian <[email protected]> remove conversion file Signed-off-by: Ignacio Hagopian <[email protected]> remove unused method Signed-off-by: Ignacio Hagopian <[email protected]> tree: add comment to explain different strategies on leaf node value updating Signed-off-by: Ignacio Hagopian <[email protected]>
1 parent 5b4132d commit 7f90724

File tree

3 files changed

+137
-268
lines changed

3 files changed

+137
-268
lines changed

conversion.go

Lines changed: 0 additions & 159 deletions
This file was deleted.

tree.go

Lines changed: 127 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"encoding/json"
3131
"errors"
3232
"fmt"
33+
"runtime"
34+
"sync"
3335

3436
"github.com/crate-crypto/go-ipa/banderwagon"
3537
)
@@ -200,6 +202,7 @@ func (n *InternalNode) toExportable() *ExportableInternalNode {
200202
case *InternalNode:
201203
exportable.Children[i] = child.toExportable()
202204
case *LeafNode:
205+
child.Commit()
203206
exportable.Children[i] = &ExportableLeafNode{
204207
Stem: child.stem,
205208
Values: child.values,
@@ -248,47 +251,14 @@ func NewStatelessInternal(depth byte, comm *Point) VerkleNode {
248251

249252
// New creates a new leaf node
250253
func NewLeafNode(stem []byte, values [][]byte) *LeafNode {
251-
cfg := GetConfig()
252-
253-
// C1.
254-
var c1poly [NodeWidth]Fr
255-
var c1 *Point
256-
count := fillSuffixTreePoly(c1poly[:], values[:NodeWidth/2])
257-
containsEmptyCodeHash := len(c1poly) >= EmptyCodeHashSecondHalfIdx &&
258-
c1poly[EmptyCodeHashFirstHalfIdx].Equal(&EmptyCodeHashFirstHalfValue) &&
259-
c1poly[EmptyCodeHashSecondHalfIdx].Equal(&EmptyCodeHashSecondHalfValue)
260-
if containsEmptyCodeHash {
261-
// Clear out values of the cached point.
262-
c1poly[EmptyCodeHashFirstHalfIdx] = FrZero
263-
c1poly[EmptyCodeHashSecondHalfIdx] = FrZero
264-
// Calculate the remaining part of c1 and add to the base value.
265-
partialc1 := cfg.CommitToPoly(c1poly[:], NodeWidth-count-2)
266-
c1 = new(Point)
267-
c1.Add(&EmptyCodeHashPoint, partialc1)
268-
} else {
269-
c1 = cfg.CommitToPoly(c1poly[:], NodeWidth-count)
270-
}
271-
272-
// C2.
273-
var c2poly [NodeWidth]Fr
274-
count = fillSuffixTreePoly(c2poly[:], values[NodeWidth/2:])
275-
c2 := cfg.CommitToPoly(c2poly[:], NodeWidth-count)
276-
277-
// Root commitment preparation for calculation.
278-
stem = stem[:StemSize] // enforce a 31-byte length
279-
var poly [NodeWidth]Fr
280-
poly[0].SetUint64(1)
281-
StemFromBytes(&poly[1], stem)
282-
toFrMultiple([]*Fr{&poly[2], &poly[3]}, []*Point{c1, c2})
283-
284254
return &LeafNode{
285255
// depth will be 0, but the commitment calculation
286256
// does not need it, and so it won't be free.
287257
values: values,
288258
stem: stem,
289-
commitment: cfg.CommitToPoly(poly[:], NodeWidth-4),
290-
c1: c1,
291-
c2: c2,
259+
commitment: nil,
260+
c1: nil,
261+
c2: nil,
292262
}
293263
}
294264

@@ -654,11 +624,33 @@ func (n *InternalNode) fillLevels(levels [][]*InternalNode) {
654624
}
655625
}
656626

627+
func (n *InternalNode) findNewLeafNodes(newLeaves []*LeafNode) []*LeafNode {
628+
for idx := range n.cow {
629+
child := n.children[idx]
630+
if childInternalNode, ok := child.(*InternalNode); ok && len(childInternalNode.cow) > 0 {
631+
newLeaves = childInternalNode.findNewLeafNodes(newLeaves)
632+
} else if leafNode, ok := child.(*LeafNode); ok {
633+
if leafNode.commitment == nil {
634+
newLeaves = append(newLeaves, leafNode)
635+
}
636+
}
637+
}
638+
return newLeaves
639+
}
640+
657641
func (n *InternalNode) Commit() *Point {
658642
if len(n.cow) == 0 {
659643
return n.commitment
660644
}
661645

646+
// New leaf nodes.
647+
newLeaves := make([]*LeafNode, 0, 64)
648+
newLeaves = n.findNewLeafNodes(newLeaves)
649+
if len(newLeaves) > 0 {
650+
batchCommitLeafNodes(newLeaves)
651+
}
652+
653+
// Internal nodes.
662654
internalNodeLevels := make([][]*InternalNode, StemSize)
663655
n.fillLevels(internalNodeLevels)
664656

@@ -1027,6 +1019,14 @@ func (n *LeafNode) updateCn(index byte, value []byte, c *Point) {
10271019
}
10281020

10291021
func (n *LeafNode) updateLeaf(index byte, value []byte) {
1022+
// If the commitment is nil, it means this is a new leaf.
1023+
// We just update the value since the commitment of all new leaves will
1024+
// be calculated when calling Commit().
1025+
if n.commitment == nil {
1026+
n.values[index] = value
1027+
return
1028+
}
1029+
10301030
// Update the corresponding C1 or C2 commitment.
10311031
var c *Point
10321032
var oldC Point
@@ -1051,6 +1051,19 @@ func (n *LeafNode) updateLeaf(index byte, value []byte) {
10511051
}
10521052

10531053
func (n *LeafNode) updateMultipleLeaves(values [][]byte) {
1054+
// If the leaf node commitment is nil, it means this is a new leaf.
1055+
// We just update the provided value in the right slot, and we're done.
1056+
// The commitment will be calculated when the tree calls Commit().
1057+
if n.commitment == nil {
1058+
for i, v := range values {
1059+
if len(v) != 0 && !bytes.Equal(v, n.values[i]) {
1060+
n.values[i] = v
1061+
}
1062+
}
1063+
return
1064+
}
1065+
1066+
// If the n.commitment isn't nil, we do diff updating.
10541067
var oldC1, oldC2 *Point
10551068

10561069
// We iterate the values, and we update the C1 and/or C2 commitments depending on the index.
@@ -1224,6 +1237,10 @@ func (n *LeafNode) Commitment() *Point {
12241237
}
12251238

12261239
func (n *LeafNode) Commit() *Point {
1240+
if n.commitment == nil {
1241+
commitLeafNodes([]*LeafNode{n})
1242+
}
1243+
12271244
return n.commitment
12281245
}
12291246

@@ -1405,6 +1422,7 @@ func (n *LeafNode) GetProofItems(keys keylist) (*ProofElements, []byte, [][]byte
14051422
// Serialize serializes a LeafNode.
14061423
// The format is: <nodeType><stem><bitlist><c1comm><c2comm><children...>
14071424
func (n *LeafNode) Serialize() ([]byte, error) {
1425+
n.Commit()
14081426
cBytes := banderwagon.ElementsToBytes([]*banderwagon.Element{n.c1, n.c2})
14091427
return n.serializeWithCompressedCommitments(cBytes[0], cBytes[1]), nil
14101428
}
@@ -1635,3 +1653,76 @@ func (n *LeafNode) serializeWithCompressedCommitments(c1Bytes [32]byte, c2Bytes
16351653

16361654
return result
16371655
}
1656+
1657+
func batchCommitLeafNodes(leaves []*LeafNode) {
1658+
minBatchSize := 8
1659+
if len(leaves) < minBatchSize {
1660+
commitLeafNodes(leaves)
1661+
return
1662+
}
1663+
1664+
batchSize := len(leaves) / runtime.NumCPU()
1665+
if batchSize < minBatchSize {
1666+
batchSize = minBatchSize
1667+
}
1668+
1669+
var wg sync.WaitGroup
1670+
for start := 0; start < len(leaves); start += batchSize {
1671+
end := start + batchSize
1672+
if end > len(leaves) {
1673+
end = len(leaves)
1674+
}
1675+
wg.Add(1)
1676+
go func(leaves []*LeafNode) {
1677+
defer wg.Done()
1678+
commitLeafNodes(leaves)
1679+
}(leaves[start:end])
1680+
}
1681+
wg.Wait()
1682+
}
1683+
1684+
func commitLeafNodes(leaves []*LeafNode) {
1685+
cfg := GetConfig()
1686+
1687+
c1c2points := make([]*Point, 2*len(leaves))
1688+
c1c2frs := make([]*Fr, 2*len(leaves))
1689+
for i, n := range leaves {
1690+
// C1.
1691+
var c1poly [NodeWidth]Fr
1692+
count := fillSuffixTreePoly(c1poly[:], n.values[:NodeWidth/2])
1693+
containsEmptyCodeHash := len(c1poly) >= EmptyCodeHashSecondHalfIdx &&
1694+
c1poly[EmptyCodeHashFirstHalfIdx].Equal(&EmptyCodeHashFirstHalfValue) &&
1695+
c1poly[EmptyCodeHashSecondHalfIdx].Equal(&EmptyCodeHashSecondHalfValue)
1696+
if containsEmptyCodeHash {
1697+
// Clear out values of the cached point.
1698+
c1poly[EmptyCodeHashFirstHalfIdx] = FrZero
1699+
c1poly[EmptyCodeHashSecondHalfIdx] = FrZero
1700+
// Calculate the remaining part of c1 and add to the base value.
1701+
partialc1 := cfg.CommitToPoly(c1poly[:], NodeWidth-count-2)
1702+
n.c1 = new(Point)
1703+
n.c1.Add(&EmptyCodeHashPoint, partialc1)
1704+
} else {
1705+
n.c1 = cfg.CommitToPoly(c1poly[:], NodeWidth-count)
1706+
}
1707+
1708+
// C2.
1709+
var c2poly [NodeWidth]Fr
1710+
count = fillSuffixTreePoly(c2poly[:], n.values[NodeWidth/2:])
1711+
n.c2 = cfg.CommitToPoly(c2poly[:], NodeWidth-count)
1712+
1713+
c1c2points[2*i], c1c2points[2*i+1] = n.c1, n.c2
1714+
c1c2frs[2*i], c1c2frs[2*i+1] = new(Fr), new(Fr)
1715+
}
1716+
1717+
toFrMultiple(c1c2frs, c1c2points)
1718+
1719+
var poly [NodeWidth]Fr
1720+
poly[0].SetUint64(1)
1721+
for i, nv := range leaves {
1722+
StemFromBytes(&poly[1], nv.stem)
1723+
poly[2] = *c1c2frs[2*i]
1724+
poly[3] = *c1c2frs[2*i+1]
1725+
1726+
nv.commitment = cfg.CommitToPoly(poly[:], 252)
1727+
}
1728+
}

0 commit comments

Comments
 (0)