@@ -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
250253func 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+
657641func (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
10291021func (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
10531053func (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
12261239func (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...>
14071424func (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