@@ -71,7 +71,7 @@ function IndexedMerkleMap(height: number): typeof IndexedMerkleMapBase {
7171}
7272
7373const provableBase = {
74- root : Field ,
74+ _internalRoot : Field ,
7575 length : Field ,
7676 data : Unconstrained . withEmpty ( {
7777 nodes : [ ] as ( bigint | undefined ) [ ] [ ] ,
@@ -81,7 +81,7 @@ const provableBase = {
8181
8282class IndexedMerkleMapBase {
8383 // data defining the provable interface of a tree
84- root : Field ;
84+ _internalRoot : Field ;
8585 length : Field ; // length of the leaves array
8686
8787 // static data defining constraints
@@ -102,6 +102,14 @@ class IndexedMerkleMapBase {
102102 readonly sortedLeaves : StoredLeaf [ ] ;
103103 } > ;
104104
105+ /**
106+ * Public getter for the root(commitment) that combines the internal root of the indexed merkle tree and length.
107+ * This provides protection against a attack vector mentioned in https://github.com/o1-labs/o1js/pull/2114#issuecomment-2948339955
108+ */
109+ get root ( ) : Field {
110+ return Poseidon . hash ( [ this . _internalRoot , this . length ] ) ;
111+ }
112+
105113 // we'd like to do `abstract static provable` here but that's not supported
106114 static provable : Provable < IndexedMerkleMapBase , InferValue < typeof provableBase > > =
107115 undefined as any ;
@@ -120,7 +128,7 @@ class IndexedMerkleMapBase {
120128 let firstLeaf = IndexedMerkleMapBase . _firstLeaf ;
121129 let firstNode = Leaf . hashNode ( firstLeaf ) . toBigInt ( ) ;
122130 let root = Nodes . setLeaf ( nodes , 0 , firstNode ) ;
123- this . root = Field ( root ) ;
131+ this . _internalRoot = Field ( root ) ;
124132 this . length = Field ( 1 ) ;
125133
126134 this . data = Unconstrained . from ( { nodes, sortedLeaves : [ firstLeaf ] } ) ;
@@ -142,7 +150,7 @@ class IndexedMerkleMapBase {
142150 */
143151 clone ( ) {
144152 let cloned = new ( this . constructor as typeof IndexedMerkleMapBase ) ( ) ;
145- cloned . root = this . root ;
153+ cloned . _internalRoot = this . _internalRoot ;
146154 cloned . length = this . length ;
147155 cloned . data . updateAsProver ( ( ) => {
148156 let { nodes, sortedLeaves } = this . data . get ( ) ;
@@ -171,7 +179,7 @@ class IndexedMerkleMapBase {
171179 overwriteIf ( condition : Bool | boolean , other : IndexedMerkleMapBase ) {
172180 condition = Bool ( condition ) ;
173181
174- this . root = Provable . if ( condition , other . root , this . root ) ;
182+ this . _internalRoot = Provable . if ( condition , other . _internalRoot , this . _internalRoot ) ;
175183 this . length = Provable . if ( condition , other . length , this . length ) ;
176184 this . data . updateAsProver ( ( ) =>
177185 Bool ( condition ) . toBoolean ( ) ? other . clone ( ) . data . get ( ) : this . data . get ( )
@@ -201,7 +209,7 @@ class IndexedMerkleMapBase {
201209
202210 // update low node
203211 let newLow = { ...low , nextKey : key } ;
204- this . root = this . _proveUpdate ( newLow , lowPath ) ;
212+ this . _internalRoot = this . _proveUpdate ( newLow , lowPath ) ;
205213 this . _setLeafUnconstrained ( true , newLow ) ;
206214
207215 // create new leaf to append
@@ -213,7 +221,7 @@ class IndexedMerkleMapBase {
213221
214222 // prove empty slot in the tree, and insert our leaf
215223 let path = this . _proveEmpty ( indexBits ) ;
216- this . root = this . _proveUpdate ( leaf , path ) ;
224+ this . _internalRoot = this . _proveUpdate ( leaf , path ) ;
217225 this . length = this . length . add ( 1 ) ;
218226 this . _setLeafUnconstrained ( false , leaf ) ;
219227 }
@@ -238,7 +246,7 @@ class IndexedMerkleMapBase {
238246
239247 // update leaf
240248 let newSelf = { ...self , value } ;
241- this . root = this . _proveUpdate ( newSelf , path ) ;
249+ this . _internalRoot = this . _proveUpdate ( newSelf , path ) ;
242250 this . _setLeafUnconstrained ( true , newSelf ) ;
243251
244252 return self . value ;
@@ -275,7 +283,7 @@ class IndexedMerkleMapBase {
275283
276284 // update low node, or leave it as is
277285 let newLow = { ...low , nextKey : key } ;
278- this . root = this . _proveUpdate ( newLow , lowPath ) ;
286+ this . _internalRoot = this . _proveUpdate ( newLow , lowPath ) ;
279287 this . _setLeafUnconstrained ( true , newLow ) ;
280288
281289 // prove inclusion of this leaf if it exists
@@ -288,7 +296,7 @@ class IndexedMerkleMapBase {
288296 value,
289297 nextKey : Provable . if ( keyExists , self . nextKey , low . nextKey ) ,
290298 } ) ;
291- this . root = this . _proveUpdate ( newLeaf , path ) ;
299+ this . _internalRoot = this . _proveUpdate ( newLeaf , path ) ;
292300 this . length = Provable . if ( keyExists , this . length , this . length . add ( 1 ) ) ;
293301 this . _setLeafUnconstrained ( keyExists , newLeaf ) ;
294302
@@ -403,7 +411,7 @@ class IndexedMerkleMapBase {
403411 let node = Leaf . hashNode ( leaf ) ;
404412 // here, we don't care at which index the leaf is included, so we pass it in as unconstrained
405413 let { root, path } = this . _computeRoot ( node , leaf . index ) ;
406- root . assertEquals ( this . root , message ?? 'Leaf is not included in the tree' ) ;
414+ root . assertEquals ( this . _internalRoot , message ?? 'Leaf is not included in the tree' ) ;
407415
408416 return path ;
409417 }
@@ -416,7 +424,7 @@ class IndexedMerkleMapBase {
416424 // here, we don't care at which index the leaf is included, so we pass it in as unconstrained
417425 let { root } = this . _computeRoot ( node , leaf . index ) ;
418426 assert (
419- condition . implies ( root . equals ( this . root ) ) ,
427+ condition . implies ( root . equals ( this . _internalRoot ) ) ,
420428 message ?? 'Leaf is not included in the tree'
421429 ) ;
422430 }
@@ -429,7 +437,7 @@ class IndexedMerkleMapBase {
429437 _proveEmpty ( index : Bool [ ] ) {
430438 let node = Field ( 0n ) ;
431439 let { root, path } = this . _computeRoot ( node , index ) ;
432- root . assertEquals ( this . root , 'Leaf is not empty' ) ;
440+ root . assertEquals ( this . _internalRoot , 'Leaf is not empty' ) ;
433441
434442 return path ;
435443 }
@@ -442,7 +450,7 @@ class IndexedMerkleMapBase {
442450 _proveInclusionOrEmpty ( condition : Bool , index : Bool [ ] , leaf : BaseLeaf , message ?: string ) {
443451 let node = Provable . if ( condition , Leaf . hashNode ( leaf ) , Field ( 0n ) ) ;
444452 let { root, path } = this . _computeRoot ( node , index ) ;
445- root . assertEquals ( this . root , message ?? 'Leaf is not included in the tree' ) ;
453+ root . assertEquals ( this . _internalRoot , message ?? 'Leaf is not included in the tree' ) ;
446454
447455 return path ;
448456 }
0 commit comments