diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 08944796c0c..0990cc86751 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -53,6 +53,12 @@ export class Oracle { return witness.map(toACVMField); } + async popMint(): Promise { + const mint = await this.typedOracle.popMint(); + if (!mint) throw new Error(`No mints available`); + return mint.map(toACVMField); + } + async getNotes( [storageSlot]: ACVMField[], [numSelects]: ACVMField[], diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index a864471a722..9782d533b04 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -80,6 +80,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + popMint(): Promise { + throw new Error('Not available.'); + } + getNotes( _storageSlot: Fr, _numSelects: number, diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 6dd86e95d45..cdbb1a0127a 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -35,6 +35,12 @@ export interface DBOracle extends CommitmentsDB { */ getAuthWitness(messageHash: Fr): Promise; + /** + * Retrieve a mint from the pez dispenser. + * @returns A promise that resolves to an array of field elements representing the mint. + */ + popMint(): Promise; + /** * Retrieve the secret key associated with a specific public key. * The function only allows access to the secret keys of the transaction creator, diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 53a47c04ff0..b33f08c2f9b 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -57,7 +57,16 @@ export class ViewDataOracle extends TypedOracle { } /** - * Gets some notes for a storage slot. + * Pops a mint from the PEZ dispenser + * @returns The mint values + */ + public popMint(): Promise { + return this.db.popMint(); + } + + /** + * Gets some notes for a contract address and storage slot. + * Returns a flattened array containing real-note-count and note preimages. * * @remarks * Check for pending notes with matching slot. diff --git a/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr b/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr index 7bf3aec2735..bec09840995 100644 --- a/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr +++ b/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr @@ -2,10 +2,8 @@ use dep::aztec::context::{PrivateContext, PublicContext, Context}; use dep::aztec::state_vars::public_state::PublicState; use dep::aztec::types::type_serialization::TypeSerializationInterface; use dep::aztec::oracle::storage::{storage_read, storage_write}; -use dep::aztec::oracle::debug_log::debug_log_format; use dep::std::hash::pedersen_hash; - use dep::std::option::Option; // The epoch length is just a random number for now. @@ -59,6 +57,10 @@ struct SlowUpdateProof { after: SlowUpdateInner, } +pub fn deserialize_slow_update_proof(serialized: [Field; M]) -> SlowUpdateProof { + SlowUpdateProof::deserialize(serialized) +} + impl SlowUpdateProof { pub fn serialize(self: Self) -> pub [Field; M] { let mut serialized = [0; M]; @@ -250,7 +252,6 @@ impl SlowMap { let fields = root.serialize(); storage_write(self.storage_slot, fields); } - } pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { @@ -267,4 +268,4 @@ pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) current = pedersen_hash([hash_left, hash_right]); }; current -} \ No newline at end of file +} diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index 2a1f5235b4e..686fea5a906 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -35,6 +35,10 @@ export abstract class BaseWallet implements Wallet { abstract createAuthWitness(message: Fr): Promise; + addMint(mint: Fr[]): Promise { + return this.pxe.addMint(mint); + } + registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise { return this.pxe.registerAccount(privKey, partialAddress); } diff --git a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts index 185897fde75..9b27eed38bd 100644 --- a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts +++ b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts @@ -1,4 +1,4 @@ -import { CheatCodes, Fr, Wallet, sleep } from '@aztec/aztec.js'; +import { CheatCodes, Fr, Wallet } from '@aztec/aztec.js'; import { CircuitsWasm } from '@aztec/circuits.js'; import { DebugLogger } from '@aztec/foundation/log'; import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; @@ -42,6 +42,10 @@ describe('e2e_slow_tree', () => { }; }; + const getMembershipMint = (proof: { index: bigint; value: Fr; sibling_path: Fr[] }) => { + return [new Fr(proof.index), proof.value, ...proof.sibling_path]; + }; + const getUpdateProof = async (newValue: bigint, index: bigint) => { const beforeProof = await getMembershipProof(index, false); const afterProof = await getMembershipProof(index, true); @@ -57,6 +61,22 @@ describe('e2e_slow_tree', () => { }; }; + const getUpdateMint = (proof: { + index: bigint; + new_value: bigint; + before: { value: Fr; sibling_path: Fr[] }; + after: { value: Fr; sibling_path: Fr[] }; + }) => { + return [ + new Fr(proof.index), + new Fr(proof.new_value), + proof.before.value, + ...proof.before.sibling_path, + proof.after.value, + ...proof.after.sibling_path, + ]; + }; + const status = async (key: bigint) => { logger(`\tTime: ${await cheatCodes.eth.timestamp()}`); logger('\tRoot', await contract.methods.un_read_root(owner).view()); @@ -70,11 +90,8 @@ describe('e2e_slow_tree', () => { logger('Initial state'); await status(key); - - await contract.methods - .read_at({ ...(await getMembershipProof(key, true)), value: 0n }) - .send() - .wait(); + await wallet.addMint(getMembershipMint(await getMembershipProof(key, true))); + await contract.methods.read_at(key).send().wait(); logger(`Updating tree[${key}] to 1 from public`); await contract.methods @@ -86,7 +103,8 @@ describe('e2e_slow_tree', () => { const zeroProof = await getMembershipProof(key, false); logger(`"Reads" tree[${zeroProof.index}] from the tree, equal to ${zeroProof.value}`); - await contract.methods.read_at(zeroProof).send().wait(); + await wallet.addMint(getMembershipMint({ ...zeroProof, value: new Fr(0) })); + await contract.methods.read_at(key).send().wait(); // Progress time to beyond the update and thereby commit it to the tree. await cheatCodes.aztec.warp((await cheatCodes.eth.timestamp()) + 1000); @@ -97,20 +115,18 @@ describe('e2e_slow_tree', () => { logger( `Tries to "read" tree[${zeroProof.index}] from the tree, but is rejected as value is not ${zeroProof.value}`, ); - await expect(contract.methods.read_at(zeroProof).simulate()).rejects.toThrowError( + await wallet.addMint(getMembershipMint({ ...zeroProof, value: new Fr(0) })); + await expect(contract.methods.read_at(key).simulate()).rejects.toThrowError( 'Assertion failed: Root does not match expected', ); + logger(`"Reads" tree[${key}], expect to be 1`); - await contract.methods - .read_at({ ...(await getMembershipProof(key, false)), value: 1n }) - .send() - .wait(); + await wallet.addMint(getMembershipMint({ ...zeroProof, value: new Fr(1) })); + await contract.methods.read_at(key).send().wait(); logger(`Updating tree[${key}] to 4 from private`); - await contract.methods - .update_at_private(await getUpdateProof(4n, key)) - .send() - .wait(); + await wallet.addMint(getUpdateMint(await getUpdateProof(4n, key))); + await contract.methods.update_at_private(key, 4n).send().wait(); await tree.updateLeaf(new Fr(4).toBuffer(), key); await status(key); diff --git a/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/main.nr index 34e7b7787dc..ea39bce9254 100644 --- a/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/main.nr @@ -1,3 +1,6 @@ +mod pez; +mod types; + // This contract allow us to "read" public storage in private through a delayed tree. // More documentation need to be outlined for this properly, but there is some in // https://github.com/AztecProtocol/aztec-packages/pull/2732/files @@ -22,9 +25,12 @@ contract SlowTree { }, }; use dep::slow_updates_tree::slow_map::{ - SlowMap, Leaf, SlowUpdateProof, compute_merkle_root + SlowMap, Leaf, SlowUpdateProof, compute_merkle_root, deserialize_slow_update_proof }; + use crate::pez::pop_mint; + use crate::types::{MembershipProof, deserialize_membership_proof}; + global TREE_HEIGHT: Field = 254; global MEMBERSHIP_SIZE: Field = 256; // TREE_HEIGHT + 2 global UPDATE_SIZE: Field = 512; // TREE_HEIGHT * 2 + 4 @@ -33,39 +39,6 @@ contract SlowTree { trees: Map>, } - // A single inclusion proof. - // M = N + 2 - struct MembershipProof { - index: Field, - value: Field, - sibling_path: [Field; N], - - } - - impl MembershipProof { - pub fn serialize(self: Self) -> pub [Field; M] { - let mut serialized = [0; M]; - serialized[0] = self.index; - serialized[1] = self.value; - for i in 0..N { - serialized[2 + i] = self.sibling_path[i]; - } - serialized - } - - pub fn deserialize(serialized: [Field; M]) -> pub Self { - let mut sibling_path = [0; N]; - for i in 0..N { - sibling_path[i] = serialized[2 + i]; - } - MembershipProof { - index: serialized[0], - value: serialized[1], - sibling_path, - } - } - } - impl Storage { fn init(context: Context) -> pub Self { Storage { @@ -102,7 +75,11 @@ contract SlowTree { } #[aztec(private)] - fn read_at(p: MembershipProof) -> Field { + fn read_at(index: Field) -> Field { + let fields = pop_mint(); + let p: MembershipProof = deserialize_membership_proof(fields); + assert(index == p.index, "Index does not match expected"); + let expected_root = compute_merkle_root(p.value, p.index, p.sibling_path); let selector = compute_selector("_assert_current_root(Field,Field)"); context.call_public_function( @@ -126,7 +103,12 @@ contract SlowTree { } #[aztec(private)] - fn update_at_private(p: SlowUpdateProof) { + fn update_at_private(index: Field, new_value: Field) { + let fields = pop_mint(); + let p: SlowUpdateProof = deserialize_slow_update_proof(fields); + assert(index == p.index, "Index does not match expected"); + assert(new_value == p.new_value, "New value does not match expected"); + // We compute the root before. let before_root = compute_merkle_root(p.before.value, p.index, p.before.sibling_path); let after_root = compute_merkle_root(p.after.value, p.index, p.after.sibling_path); diff --git a/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/pez.nr b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/pez.nr new file mode 100644 index 00000000000..06688152bf2 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/pez.nr @@ -0,0 +1,6 @@ +#[oracle(popMint)] +fn pop_mint_oracle() -> [Field; N] {} + +unconstrained pub fn pop_mint() -> [Field; N] { + pop_mint_oracle() +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/types.nr b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/types.nr new file mode 100644 index 00000000000..f9a86eaa096 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/slow_tree_contract/src/types.nr @@ -0,0 +1,36 @@ +// A single inclusion proof. +// M = N + 2 +struct MembershipProof { + index: Field, + value: Field, + sibling_path: [Field; N], +} + +pub fn deserialize_membership_proof(serialized: [Field; M]) -> pub MembershipProof { + let mut sibling_path = [0; N]; + for i in 0..N { + sibling_path[i] = serialized[2 + i]; + } + MembershipProof { + index: serialized[0], + value: serialized[1], + sibling_path, + } +} + + +impl MembershipProof { + pub fn serialize(self: Self) -> pub [Field; M] { + let mut serialized = [0; M]; + serialized[0] = self.index; + serialized[1] = self.value; + for i in 0..N { + serialized[2 + i] = self.sibling_path[i]; + } + serialized + } + + pub fn deserialize(serialized: [Field; M]) -> Self { + deserialize_membership_proof(serialized) + } +} \ No newline at end of file diff --git a/yarn-project/pxe/src/database/database.ts b/yarn-project/pxe/src/database/database.ts index dc99768ffcc..7ba2f05b6fc 100644 --- a/yarn-project/pxe/src/database/database.ts +++ b/yarn-project/pxe/src/database/database.ts @@ -24,6 +24,18 @@ export interface Database extends ContractDatabase { */ getAuthWitness(messageHash: Fr): Promise; + /** + * Adding a mint to the pez dispenser. + * @param mint - An array of field elements representing the mint. + */ + addMint(mint: Fr[]): Promise; + + /** + * Get the next mint from the pez dispenser. + * @returns A promise that resolves to an array of field elements representing the mint. + */ + popMint(): Promise; + /** * Gets notes based on the provided filter. * @param filter - The filter to apply to the notes. diff --git a/yarn-project/pxe/src/database/memory_db.ts b/yarn-project/pxe/src/database/memory_db.ts index bd604249757..1ff2047d4a4 100644 --- a/yarn-project/pxe/src/database/memory_db.ts +++ b/yarn-project/pxe/src/database/memory_db.ts @@ -20,6 +20,7 @@ export class MemoryDB extends MemoryContractDatabase implements Database { private globalVariablesHash: Fr | undefined; private addresses: CompleteAddress[] = []; private authWitnesses: Record = {}; + private pezDispenser: Fr[][] = []; constructor(logSuffix?: string) { super(createDebugLogger(logSuffix ? 'aztec:memory_db_' + logSuffix : 'aztec:memory_db')); @@ -44,11 +45,20 @@ export class MemoryDB extends MemoryContractDatabase implements Database { return Promise.resolve(this.authWitnesses[messageHash.toString()]); } - public addNote(note: NoteDao) { + public addNote(note: NoteDao): Promise { this.notesTable.push(note); return Promise.resolve(); } + public addMint(mint: Fr[]): Promise { + this.pezDispenser.push(mint); + return Promise.resolve(); + } + + public popMint(): Promise { + return Promise.resolve(this.pezDispenser.pop()); + } + public addNotes(notes: NoteDao[]) { this.notesTable.push(...notes); return Promise.resolve(); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 4938f6c02b4..15ff0cad51d 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -120,6 +120,10 @@ export class PXEService implements PXE { return this.db.addAuthWitness(witness.requestHash, witness.witness); } + public addMint(mint: Fr[]) { + return this.db.addMint(mint); + } + public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise { const completeAddress = await CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress); const wasAdded = await this.db.addCompleteAddress(completeAddress); diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index fb2580b9cbe..baf7ca82056 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -44,6 +44,12 @@ export class SimulatorOracle implements DBOracle { return witness; } + async popMint(): Promise { + const mint = await this.db.popMint(); + if (!mint) throw new Error(`No mints available`); + return mint; + } + async getNotes(contractAddress: AztecAddress, storageSlot: Fr) { const noteDaos = await this.db.getNotes({ contractAddress, storageSlot }); return noteDaos.map(({ contractAddress, storageSlot, nonce, note, innerNoteHash, siloedNullifier, index }) => ({ diff --git a/yarn-project/types/src/interfaces/pxe.ts b/yarn-project/types/src/interfaces/pxe.ts index b6e7bd0e3cd..a562d189179 100644 --- a/yarn-project/types/src/interfaces/pxe.ts +++ b/yarn-project/types/src/interfaces/pxe.ts @@ -42,6 +42,12 @@ export interface PXE { */ addAuthWitness(authWitness: AuthWitness): Promise; + /** + * Adding a mint to the pez dispenser. + * @param mint - An array of field elements representing the mint. + */ + addMint(mint: Fr[]): Promise; + /** * Registers a user account in PXE given its master encryption private key. * Once a new account is registered, the PXE Service will trial-decrypt all published notes on