From 4900b4786fbbb97432a68d8785c8d3f55d2580b6 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 11 Apr 2024 15:09:57 +0300 Subject: [PATCH 1/5] fix empty hahs passing --- src/lib/provable/merkle-list.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 2286caee40..b7ebbc8102 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -326,12 +326,17 @@ class MerkleListIterator implements MerkleListIteratorBase { // fixed parts readonly data: Unconstrained[]>; readonly hash: Field; + readonly emptyHash: Field; // mutable parts currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleListIteratorBase) { + constructor( + value: MerkleListIteratorBase & { + emptyHash: Field; + } + ) { Object.assign(this, value); } @@ -400,6 +405,7 @@ class MerkleListIterator implements MerkleListIteratorBase { hash: this.hash, currentHash: this.currentHash, currentIndex, + emptyHash, }); } @@ -448,6 +454,7 @@ class MerkleListIterator implements MerkleListIteratorBase { hash, currentHash: hash, currentIndex: Unconstrained.from(0), + emptyHash: emptyHash_, }); } @@ -468,7 +475,8 @@ class MerkleListIterator implements MerkleListIteratorBase { static createFromList(merkleList: typeof MerkleList) { return this.create( merkleList.prototype.innerProvable, - merkleList._nextHash + merkleList._nextHash, + merkleList.emptyHash ); } From a80ef2c6b12ed59515464c0bdbfa7c529b61d6c5 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 11 Apr 2024 15:13:24 +0300 Subject: [PATCH 2/5] remove unused property --- src/lib/provable/merkle-list.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index b7ebbc8102..8cb2265975 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -326,7 +326,6 @@ class MerkleListIterator implements MerkleListIteratorBase { // fixed parts readonly data: Unconstrained[]>; readonly hash: Field; - readonly emptyHash: Field; // mutable parts currentHash: Field; From 6c2992de48f29495872d5182c172543a12a3f3f3 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 11 Apr 2024 15:47:53 +0300 Subject: [PATCH 3/5] remove unneeded additions --- .../zkapps/reducer/actions-as-merkle-list.ts | 126 ++---------------- src/lib/provable/merkle-list.ts | 8 +- src/mina | 2 +- 3 files changed, 13 insertions(+), 123 deletions(-) diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 16ce8ea29c..9784d5d4dc 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -22,8 +22,8 @@ import { const { Actions } = AccountUpdate; // in this example, an action is just a public key -type Action = PublicKey; -const Action = PublicKey; +type Action = Field; +const Action = Field; // the actions within one account update are a Merkle list with a custom hash const emptyHash = Actions.empty().hash; @@ -32,117 +32,13 @@ const nextHash = (hash: Field, action: Action) => class MerkleActions extends MerkleList.create(Action, nextHash, emptyHash) {} -// the "action state" / actions from many account updates is a Merkle list -// of the above Merkle list, with another custom hash -let emptyActionsHash = Actions.emptyActionState(); -const nextActionsHash = (hash: Field, actions: MerkleActions) => - Actions.updateSequenceState(hash, actions.hash); +let list = MerkleActions.from([Field(0), Field(1), Field(2), Field(3)]); +let iter = list.startIterating(); -class MerkleActionss extends MerkleList.create( - MerkleActions.provable, - nextActionsHash, - emptyActionsHash -) {} - -// constants for our static-sized provable code -const MAX_UPDATES_WITH_ACTIONS = 100; -const MAX_ACTIONS_PER_UPDATE = 2; - -/** - * This contract allows you to push either 1 or 2 public keys as actions, - * and has a reducer-like method which checks whether a given public key is contained in those actions. - */ -class ActionsContract extends SmartContract { - reducer = Reducer({ actionType: Action }); - - @method - async postAddress(address: PublicKey) { - this.reducer.dispatch(address); - } - - // to exhibit the generality of reducer: can dispatch more than 1 action per account update - @method async postTwoAddresses(a1: PublicKey, a2: PublicKey) { - this.reducer.dispatch(a1); - this.reducer.dispatch(a2); - } - - @method - async assertContainsAddress(address: PublicKey) { - // get actions and, in a witness block, wrap them in a Merkle list of lists - - // note: need to reverse here because `getActions()` returns the last pushed action last, - // but MerkleList.from() wants it to be first to match the natural iteration order - let actionss = this.reducer.getActions().reverse(); - - let merkleActionss = Provable.witness(MerkleActionss.provable, () => - MerkleActionss.from(actionss.map((as) => MerkleActions.from(as))) - ); - - // prove that we know the correct action state - this.account.actionState.requireEquals(merkleActionss.hash); - - // now our provable code to process the actions is very straight-forward - // (note: if we're past the actual sizes, `.pop()` returns a dummy Action -- in this case, the "empty" public key which is not equal to any real address) - let hasAddress = Bool(false); - - for (let i = 0; i < MAX_UPDATES_WITH_ACTIONS; i++) { - let merkleActions = merkleActionss.pop(); - - for (let j = 0; j < MAX_ACTIONS_PER_UPDATE; j++) { - let action = merkleActions.pop(); - hasAddress = hasAddress.or(action.equals(address)); - } - } - - assert(hasAddress); - } -} - -// TESTS - -// set up a local blockchain - -let Local = Mina.LocalBlockchain({ proofsEnabled: false }); -Mina.setActiveInstance(Local); - -let [ - { publicKey: sender, privateKey: senderKey }, - { publicKey: zkappAddress, privateKey: zkappKey }, - { publicKey: otherAddress }, - { publicKey: anotherAddress }, -] = Local.testAccounts; - -let zkapp = new ActionsContract(zkappAddress); - -// deploy the contract - -await ActionsContract.compile(); -console.log( - `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, - (await ActionsContract.analyzeMethods()).assertContainsAddress.rows -); -let deployTx = await Mina.transaction(sender, async () => zkapp.deploy()); -await deployTx.sign([senderKey, zkappKey]).send(); - -// push some actions - -let dispatchTx = await Mina.transaction(sender, async () => { - await zkapp.postAddress(otherAddress); - await zkapp.postAddress(zkappAddress); - await zkapp.postTwoAddresses(anotherAddress, sender); - await zkapp.postAddress(anotherAddress); - await zkapp.postTwoAddresses(zkappAddress, otherAddress); -}); -await dispatchTx.prove(); -await dispatchTx.sign([senderKey]).send(); - -assert(zkapp.reducer.getActions().length === 5); - -// check if the actions contain the `sender` address - -Local.setProofsEnabled(true); -let containsTx = await Mina.transaction(sender, () => - zkapp.assertContainsAddress(sender) -); -await containsTx.prove(); -await containsTx.sign([senderKey]).send(); +Provable.log(iter.next()); +Provable.log(iter.next()); +Provable.log(iter.next()); +Provable.log(iter.next()); +Provable.log(iter.next()); +Provable.log(iter.next()); +Provable.log(iter.next()); diff --git a/src/lib/provable/merkle-list.ts b/src/lib/provable/merkle-list.ts index 8cb2265975..0b28c62266 100644 --- a/src/lib/provable/merkle-list.ts +++ b/src/lib/provable/merkle-list.ts @@ -331,11 +331,7 @@ class MerkleListIterator implements MerkleListIteratorBase { currentHash: Field; currentIndex: Unconstrained; - constructor( - value: MerkleListIteratorBase & { - emptyHash: Field; - } - ) { + constructor(value: MerkleListIteratorBase) { Object.assign(this, value); } @@ -404,7 +400,6 @@ class MerkleListIterator implements MerkleListIteratorBase { hash: this.hash, currentHash: this.currentHash, currentIndex, - emptyHash, }); } @@ -453,7 +448,6 @@ class MerkleListIterator implements MerkleListIteratorBase { hash, currentHash: hash, currentIndex: Unconstrained.from(0), - emptyHash: emptyHash_, }); } diff --git a/src/mina b/src/mina index 43ab0db62d..9652edc248 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 43ab0db62dd8423a5e7a88cd99abd8957476075f +Subproject commit 9652edc24827697f7cd8beffaf13b23c15f4fefd From 33f33697a47ebef731580903ce91e8da600de835 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 11 Apr 2024 15:49:15 +0300 Subject: [PATCH 4/5] undo dummy changes --- .../zkapps/reducer/actions-as-merkle-list.ts | 126 ++++++++++++++++-- 1 file changed, 115 insertions(+), 11 deletions(-) diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts index 9784d5d4dc..16ce8ea29c 100644 --- a/src/examples/zkapps/reducer/actions-as-merkle-list.ts +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -22,8 +22,8 @@ import { const { Actions } = AccountUpdate; // in this example, an action is just a public key -type Action = Field; -const Action = Field; +type Action = PublicKey; +const Action = PublicKey; // the actions within one account update are a Merkle list with a custom hash const emptyHash = Actions.empty().hash; @@ -32,13 +32,117 @@ const nextHash = (hash: Field, action: Action) => class MerkleActions extends MerkleList.create(Action, nextHash, emptyHash) {} -let list = MerkleActions.from([Field(0), Field(1), Field(2), Field(3)]); -let iter = list.startIterating(); +// the "action state" / actions from many account updates is a Merkle list +// of the above Merkle list, with another custom hash +let emptyActionsHash = Actions.emptyActionState(); +const nextActionsHash = (hash: Field, actions: MerkleActions) => + Actions.updateSequenceState(hash, actions.hash); -Provable.log(iter.next()); -Provable.log(iter.next()); -Provable.log(iter.next()); -Provable.log(iter.next()); -Provable.log(iter.next()); -Provable.log(iter.next()); -Provable.log(iter.next()); +class MerkleActionss extends MerkleList.create( + MerkleActions.provable, + nextActionsHash, + emptyActionsHash +) {} + +// constants for our static-sized provable code +const MAX_UPDATES_WITH_ACTIONS = 100; +const MAX_ACTIONS_PER_UPDATE = 2; + +/** + * This contract allows you to push either 1 or 2 public keys as actions, + * and has a reducer-like method which checks whether a given public key is contained in those actions. + */ +class ActionsContract extends SmartContract { + reducer = Reducer({ actionType: Action }); + + @method + async postAddress(address: PublicKey) { + this.reducer.dispatch(address); + } + + // to exhibit the generality of reducer: can dispatch more than 1 action per account update + @method async postTwoAddresses(a1: PublicKey, a2: PublicKey) { + this.reducer.dispatch(a1); + this.reducer.dispatch(a2); + } + + @method + async assertContainsAddress(address: PublicKey) { + // get actions and, in a witness block, wrap them in a Merkle list of lists + + // note: need to reverse here because `getActions()` returns the last pushed action last, + // but MerkleList.from() wants it to be first to match the natural iteration order + let actionss = this.reducer.getActions().reverse(); + + let merkleActionss = Provable.witness(MerkleActionss.provable, () => + MerkleActionss.from(actionss.map((as) => MerkleActions.from(as))) + ); + + // prove that we know the correct action state + this.account.actionState.requireEquals(merkleActionss.hash); + + // now our provable code to process the actions is very straight-forward + // (note: if we're past the actual sizes, `.pop()` returns a dummy Action -- in this case, the "empty" public key which is not equal to any real address) + let hasAddress = Bool(false); + + for (let i = 0; i < MAX_UPDATES_WITH_ACTIONS; i++) { + let merkleActions = merkleActionss.pop(); + + for (let j = 0; j < MAX_ACTIONS_PER_UPDATE; j++) { + let action = merkleActions.pop(); + hasAddress = hasAddress.or(action.equals(address)); + } + } + + assert(hasAddress); + } +} + +// TESTS + +// set up a local blockchain + +let Local = Mina.LocalBlockchain({ proofsEnabled: false }); +Mina.setActiveInstance(Local); + +let [ + { publicKey: sender, privateKey: senderKey }, + { publicKey: zkappAddress, privateKey: zkappKey }, + { publicKey: otherAddress }, + { publicKey: anotherAddress }, +] = Local.testAccounts; + +let zkapp = new ActionsContract(zkappAddress); + +// deploy the contract + +await ActionsContract.compile(); +console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + (await ActionsContract.analyzeMethods()).assertContainsAddress.rows +); +let deployTx = await Mina.transaction(sender, async () => zkapp.deploy()); +await deployTx.sign([senderKey, zkappKey]).send(); + +// push some actions + +let dispatchTx = await Mina.transaction(sender, async () => { + await zkapp.postAddress(otherAddress); + await zkapp.postAddress(zkappAddress); + await zkapp.postTwoAddresses(anotherAddress, sender); + await zkapp.postAddress(anotherAddress); + await zkapp.postTwoAddresses(zkappAddress, otherAddress); +}); +await dispatchTx.prove(); +await dispatchTx.sign([senderKey]).send(); + +assert(zkapp.reducer.getActions().length === 5); + +// check if the actions contain the `sender` address + +Local.setProofsEnabled(true); +let containsTx = await Mina.transaction(sender, () => + zkapp.assertContainsAddress(sender) +); +await containsTx.prove(); +await containsTx.sign([senderKey]).send(); From 49b162e09f7a892e8eef376753bac929a471c7c4 Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 11 Apr 2024 15:51:55 +0300 Subject: [PATCH 5/5] reverse mina commit --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 9652edc248..43ab0db62d 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 9652edc24827697f7cd8beffaf13b23c15f4fefd +Subproject commit 43ab0db62dd8423a5e7a88cd99abd8957476075f