@@ -7,6 +7,17 @@ import { TypedClass } from "../../types";
77import { MerkleTreeStore } from "./MerkleTreeStore" ;
88import { InMemoryMerkleTreeStorage } from "./InMemoryMerkleTreeStorage" ;
99
10+ /**
11+ * More efficient version of `maybeSwapBad` which
12+ * reuses an intermediate variable
13+ */
14+ export function maybeSwap ( b : Bool , x : Field , y : Field ) : [ Field , Field ] {
15+ const m = b . toField ( ) . mul ( x . sub ( y ) ) ; // b*(x - y)
16+ const x1 = y . add ( m ) ; // y + b*(x - y)
17+ const y2 = x . sub ( m ) ; // x - b*(x - y) = x + b*(y - x)
18+ return [ x1 , y2 ] ;
19+ }
20+
1021export class StructTemplate extends Struct ( {
1122 path : Provable . Array ( Field , 0 ) ,
1223 isLeft : Provable . Array ( Bool , 0 ) ,
@@ -22,6 +33,11 @@ export interface AbstractMerkleWitness extends StructTemplate {
2233 */
2334 calculateRoot ( hash : Field ) : Field ;
2435
36+ calculateRootIncrement (
37+ index : Field ,
38+ leaf : Field
39+ ) : [ Field , AbstractMerkleWitness ] ;
40+
2541 /**
2642 * Calculates the index of the leaf node that belongs to this Witness.
2743 * @returns Index of the leaf.
@@ -119,6 +135,24 @@ export interface AbstractMerkleTreeClass {
119135 * It also holds the Witness class under tree.WITNESS
120136 */
121137export function createMerkleTree ( height : number ) : AbstractMerkleTreeClass {
138+ function generateZeroes ( ) {
139+ const zeroes = [ 0n ] ;
140+ for ( let index = 1 ; index < height ; index += 1 ) {
141+ const previousLevel = Field ( zeroes [ index - 1 ] ) ;
142+ zeroes . push ( Poseidon . hash ( [ previousLevel , previousLevel ] ) . toBigInt ( ) ) ;
143+ }
144+ return zeroes ;
145+ }
146+
147+ let zeroCache : bigint [ ] | undefined = undefined ;
148+
149+ function getZeroes ( ) {
150+ if ( zeroCache === undefined ) {
151+ zeroCache = generateZeroes ( ) ;
152+ }
153+ return zeroCache ;
154+ }
155+
122156 /**
123157 * The {@link RollupMerkleWitness} class defines a circuit-compatible base class
124158 * for [Merkle Witness'](https://computersciencewiki.org/index.php/Merkle_proof).
@@ -147,14 +181,74 @@ export function createMerkleTree(height: number): AbstractMerkleTreeClass {
147181
148182 for ( let index = 1 ; index < n ; ++ index ) {
149183 const isLeft = this . isLeft [ index - 1 ] ;
150- // eslint-disable-next-line @typescript-eslint/no-use-before-define
184+
151185 const [ left , right ] = maybeSwap ( isLeft , hash , this . path [ index - 1 ] ) ;
152186 hash = Poseidon . hash ( [ left , right ] ) ;
153187 }
154188
155189 return hash ;
156190 }
157191
192+ public calculateRootIncrement (
193+ leafIndex : Field ,
194+ leaf : Field
195+ ) : [ Field , RollupMerkleWitness ] {
196+ // This won't generate any constraints, since it's purely a computation on constants
197+ const zero = getZeroes ( ) ;
198+
199+ if ( zero . length === 0 ) {
200+ throw new Error ( "Zeroes not initialized" ) ;
201+ }
202+ const zeroes = zero . map ( ( x ) => Field ( x ) ) ;
203+
204+ let hash = leaf ;
205+ const n = this . height ( ) ;
206+
207+ let notDiverged = Bool ( true ) ;
208+ const newPath = leafIndex . add ( 1 ) . toBits ( ) ;
209+ newPath . push ( Bool ( false ) ) ;
210+
211+ const newSiblings : Field [ ] = [ ] ;
212+ const newIsLefts : Bool [ ] = [ ] ;
213+
214+ for ( let index = 0 ; index < n - 1 ; ++ index ) {
215+ const isLeft = this . isLeft [ index ] ;
216+ const sibling = this . path [ index ] ;
217+
218+ const newIsLeft = newPath [ index ] . not ( ) ;
219+
220+ // Bool(true) default for root level
221+ let convergesNextLevel = Bool ( true ) ;
222+ if ( index < n - 2 ) {
223+ convergesNextLevel = newPath [ index + 1 ]
224+ . equals ( this . isLeft [ index + 1 ] )
225+ . not ( ) ;
226+ }
227+
228+ const nextSibling = Provable . if (
229+ convergesNextLevel . and ( notDiverged ) ,
230+ hash ,
231+ Provable . if ( notDiverged , zeroes [ index ] , sibling )
232+ ) ;
233+
234+ notDiverged = notDiverged . and ( convergesNextLevel . not ( ) ) ;
235+
236+ newSiblings . push ( nextSibling ) ;
237+ newIsLefts . push ( newIsLeft ) ;
238+
239+ const [ left , right ] = maybeSwap ( isLeft , hash , sibling ) ;
240+ hash = Poseidon . hash ( [ left , right ] ) ;
241+ }
242+
243+ return [
244+ hash ,
245+ new RollupMerkleWitness ( {
246+ isLeft : newIsLefts ,
247+ path : newSiblings ,
248+ } ) ,
249+ ] ;
250+ }
251+
158252 /**
159253 * Calculates the index of the leaf node that belongs to this Witness.
160254 * @returns Index of the leaf.
@@ -215,6 +309,7 @@ export function createMerkleTree(height: number): AbstractMerkleTreeClass {
215309 } ) ;
216310 }
217311 }
312+
218313 return class AbstractRollupMerkleTree implements AbstractMerkleTree {
219314 public static HEIGHT = height ;
220315
@@ -238,13 +333,7 @@ export function createMerkleTree(height: number): AbstractMerkleTreeClass {
238333
239334 public constructor ( store : MerkleTreeStore ) {
240335 this . store = store ;
241- this . zeroes = [ 0n ] ;
242- for ( let index = 1 ; index < AbstractRollupMerkleTree . HEIGHT ; index += 1 ) {
243- const previousLevel = Field ( this . zeroes [ index - 1 ] ) ;
244- this . zeroes . push (
245- Poseidon . hash ( [ previousLevel , previousLevel ] ) . toBigInt ( )
246- ) ;
247- }
336+ this . zeroes = generateZeroes ( ) ;
248337 }
249338
250339 public assertIndexRange ( index : bigint ) {
@@ -414,14 +503,3 @@ export function createMerkleTree(height: number): AbstractMerkleTreeClass {
414503
415504export class RollupMerkleTree extends createMerkleTree ( 256 ) { }
416505export class RollupMerkleTreeWitness extends RollupMerkleTree . WITNESS { }
417-
418- /**
419- * More efficient version of `maybeSwapBad` which
420- * reuses an intermediate variable
421- */
422- export function maybeSwap ( b : Bool , x : Field , y : Field ) : [ Field , Field ] {
423- const m = b . toField ( ) . mul ( x . sub ( y ) ) ; // b*(x - y)
424- const x1 = y . add ( m ) ; // y + b*(x - y)
425- const y2 = x . sub ( m ) ; // x - b*(x - y) = x + b*(y - x)
426- return [ x1 , y2 ] ;
427- }
0 commit comments