From d389bd17121b79460ced6d4c1ca10b17587387c4 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 28 Sep 2023 10:27:12 +0000 Subject: [PATCH 01/10] feat: inital storage proofs --- .../acir-simulator/src/acvm/oracle/oracle.ts | 4 + .../src/acvm/oracle/typed_oracle.ts | 4 + .../src/client/client_execution_context.ts | 2 +- .../src/client/private_execution.test.ts | 4 +- .../src/client/view_data_oracle.ts | 16 ++- yarn-project/acir-simulator/src/public/db.ts | 10 +- .../src/public/public_execution_context.ts | 2 +- .../src/e2e_stateful_test_contract.test.ts | 101 ++++++++++++++++++ .../src/historic_commitment_oracle.nr | 22 ++++ .../stateful_test_contract/src/main.nr | 28 +++++ .../pxe/src/simulator_oracle/index.ts | 10 +- .../src/simulator/public_executor.ts | 10 +- 12 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts create mode 100644 yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index e587bb7db6c..2e3d977aee6 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -130,6 +130,10 @@ export class Oracle { return toAcvmL1ToL2MessageLoadOracleInputs(message, root); } + async getDataTreePath([commitment]: ACVMField[]): Promise { + return (await this.typedOracle.getDataTreePath(fromACVMField(commitment))).map(toACVMField); + } + async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise { const contractAddress = AztecAddress.fromString(aztecAddress); const portalContactAddress = await this.typedOracle.getPortalContractAddress(contractAddress); 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 a972474b420..2529a48a74f 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -113,6 +113,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getDataTreePath(_commitment: Fr): Promise { + throw new Error('Not available.'); + } + getPortalContractAddress(_contractAddress: AztecAddress): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index b11cc97b4e6..f8b16a289df 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -275,7 +275,7 @@ export class ClientExecutionContext extends ViewDataOracle { noteHashToLookUp = computeUniqueCommitment(wasm, nonce, noteHashToLookUp); } - const index = await this.db.getCommitmentIndex(noteHashToLookUp); + const index = await this.db.findCommitmentIndex(noteHashToLookUp.toBuffer()); const exists = index !== undefined; if (exists) { this.gotNotes.set(noteHashToLookUp.value, index); diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 59c3993460d..003553a1cd8 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -382,7 +382,7 @@ describe('Private Execution test suite', () => { const nonce = new Fr(1n); const customNoteHash = hash([toBufferBE(amount, 32), secret.toBuffer()]); const innerNoteHash = Fr.fromBuffer(hash([storageSlot.toBuffer(), customNoteHash])); - oracle.getCommitmentIndex.mockResolvedValue(2n); + oracle.findCommitmentIndex.mockResolvedValue(2n); const result = await runSimulator({ abi, @@ -734,7 +734,7 @@ describe('Private Execution test suite', () => { const storageSlot = new Fr(2); const innerNoteHash = hash([storageSlot.toBuffer(), noteHash.toBuffer()]); const siloedNoteHash = siloCommitment(wasm, contractAddress, Fr.fromBuffer(innerNoteHash)); - oracle.getCommitmentIndex.mockResolvedValue(0n); + oracle.findCommitmentIndex.mockResolvedValue(0n); const result = await runSimulator({ abi, 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 48a7466494e..9d4d0fd266c 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -114,7 +114,7 @@ export class ViewDataOracle extends TypedOracle { noteHashToLookUp = computeUniqueCommitment(wasm, nonce, noteHashToLookUp); } - const index = await this.db.getCommitmentIndex(noteHashToLookUp); + const index = await this.db.findCommitmentIndex(noteHashToLookUp.toBuffer()); return index !== undefined; } @@ -128,6 +128,20 @@ export class ViewDataOracle extends TypedOracle { return { ...message, root: this.historicBlockData.l1ToL2MessagesTreeRoot }; } + /** + * Fetches the index and data tree path for a commitment + * @param commitment - The commitment + * @returns The index and data tree path + */ + public async getDataTreePath(commitment: Fr): Promise { + const index = await this.db.findCommitmentIndex(commitment.toBuffer()); + if (!index) { + throw new Error(`Commitment ${commitment} not found in private data tree`); + } + const path = await this.db.getDataTreePath(index); + return [new Fr(index), ...path.toFieldArray()]; + } + /** * Retrieves the portal contract address associated with the given contract address. * Throws an error if the input contract address is not found or invalid. diff --git a/yarn-project/acir-simulator/src/public/db.ts b/yarn-project/acir-simulator/src/public/db.ts index 1cf022f93f5..59819601b74 100644 --- a/yarn-project/acir-simulator/src/public/db.ts +++ b/yarn-project/acir-simulator/src/public/db.ts @@ -1,6 +1,7 @@ import { EthAddress, FunctionSelector } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; +import { DataCommitmentProvider } from '@aztec/types'; import { MessageLoadOracleInputs } from '../acvm/index.js'; @@ -55,7 +56,7 @@ export interface PublicContractsDB { } /** Database interface for providing access to commitment tree and l1 to l2 messages tree (append only data trees). */ -export interface CommitmentsDB { +export interface CommitmentsDB extends DataCommitmentProvider { /** * Gets a confirmed L1 to L2 message for the given message key. * TODO(Maddiaa): Can be combined with aztec-node method that does the same thing. @@ -63,11 +64,4 @@ export interface CommitmentsDB { * @returns - The l1 to l2 message object */ getL1ToL2Message(msgKey: Fr): Promise; - - /** - * Gets the index of a commitment in the private data tree. - * @param commitment - The commitment. - * @returns - The index of the commitment. Undefined if it does not exist in the tree. - */ - getCommitmentIndex(commitment: Fr): Promise; } diff --git a/yarn-project/acir-simulator/src/public/public_execution_context.ts b/yarn-project/acir-simulator/src/public/public_execution_context.ts index 04094da9211..bdb4dab2313 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -130,7 +130,7 @@ export class PublicExecutionContext extends TypedOracle { // Once public kernel or base rollup circuit injects nonces, this can be updated to use uniqueSiloedCommitment. const wasm = await CircuitsWasm.get(); const siloedNoteHash = siloCommitment(wasm, this.execution.contractAddress, innerNoteHash); - const index = await this.commitmentsDb.getCommitmentIndex(siloedNoteHash); + const index = await this.commitmentsDb.findCommitmentIndex(siloedNoteHash.toBuffer()); return index !== undefined; } diff --git a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts new file mode 100644 index 00000000000..63c33b03e57 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -0,0 +1,101 @@ +/* eslint-disable camelcase */ +import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; +import { CheatCodes, Fr, Wallet } from '@aztec/aztec.js'; +import { CircuitsWasm, CompleteAddress } from '@aztec/circuits.js'; +import { pedersenHashInputs, pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; +import { DebugLogger } from '@aztec/foundation/log'; +import { StatefulTestContract } from '@aztec/noir-contracts/types'; +import { PXE } from '@aztec/types'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_deploy_contract', () => { + let aztecNode: AztecNodeService | undefined; + let pxe: PXE; + let accounts: CompleteAddress[]; + let logger: DebugLogger; + let wallet: Wallet; + let teardown: () => Promise; + let cheatCodes: CheatCodes; + + let statefulContract: StatefulTestContract; + + beforeEach(async () => { + ({ teardown, aztecNode, pxe, accounts, logger, wallet, cheatCodes } = await setup()); + statefulContract = await StatefulTestContract.deploy(wallet, accounts[0].address, 1).send().deployed(); + }, 100_000); + + afterEach(() => teardown()); + + it('Try to get a witness', async () => { + if (!aztecNode) { + throw new Error('No aztec node'); + } + const owner = accounts[0].address; + + console.log(`Historic: ${(await aztecNode.getHistoricBlockData()).privateDataTreeRoot.toBigInt()}`); + + const tx = statefulContract.methods.create_note(owner, 99n).send(); + const receipt = await tx.wait(); + + const storageSlot = cheatCodes.aztec.computeSlotInMap(1n, owner); + const notes = await pxe.getPrivateStorageAt(owner, statefulContract.address, storageSlot); + const note = notes[1]; + + const nonces = await pxe.getNoteNonces(statefulContract.address, storageSlot, note, receipt.txHash); + + const valueNote = { + value: note.items[0], + owner: note.items[1], + randomness: note.items[2], + header: { + contract_address: statefulContract.address, + nonce: nonces[0], + storage_slot: storageSlot, + }, + }; + + const commitment = new Fr(await statefulContract.methods.get_commitment(valueNote).view()); + console.log(`Commitment: ${commitment}`); + + const index = await aztecNode.findCommitmentIndex(commitment.toBuffer()); + console.log(`Index: ${index}`); + + const circuitsWasm = await CircuitsWasm.get(); + // pedersenPlookupCommitInputs + // const h = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenPlookupCommitInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); + const h = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenHashInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); + + const path = await statefulContract.methods.get_path(valueNote).view(); + console.log(`Path: `, path); + + const historic = await aztecNode.getHistoricBlockData(); + const root = await statefulContract.methods.get_root(valueNote).view(); + console.log(`Historic: ${historic.privateDataTreeRoot.toBigInt()}, our computed: ${root}`); + + // Computing the root from path, using the `pedersenHashInputs` + // crypto::pedersen_hash::lookup::hash_multiple function. + // Noir is using: + // crypto::pedersen_commitment::lookup::compress_native + const rootFromPath = (leaf: Fr, index: bigint, path: any[]) => { + const temps: bigint[] = []; + let node = leaf; + let i = index; + for (const sibling of path) { + if (i % 2n === 0n) { + node = h(node, new Fr(sibling)); + } else { + node = h(new Fr(sibling), node); + } + + temps.push(node.toBigInt()); + i /= 2n; + } + console.log(temps); + return node; + }; + + const myRoot = rootFromPath(commitment, index ?? 0n, path.slice()); + console.log(`Historic: ${historic.privateDataTreeRoot.toBigInt()}, myroot: ${myRoot.toBigInt()}`); + }); +}); diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr new file mode 100644 index 00000000000..4287ecfb33c --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr @@ -0,0 +1,22 @@ +use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; +use dep::aztec::utils::arr_copy_slice; + +global PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN: Field = 33; + +struct PrivateDataMembershipWitness { + index: Field, + path: [Field; PRIVATE_DATA_TREE_HEIGHT], +} + +#[oracle(getDataTreePath)] +fn get_private_data_sibling_witness_oracle(_commitment: Field) -> [Field; PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN] {} + +unconstrained fn get_private_data_sibling_witness(commitment: Field) -> PrivateDataMembershipWitness { + assert(PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN == PRIVATE_DATA_TREE_HEIGHT + 1); + let fields = get_private_data_sibling_witness_oracle(commitment); + PrivateDataMembershipWitness { + index: fields[0], + path: arr_copy_slice(fields, [0; PRIVATE_DATA_TREE_HEIGHT], 1) + } + +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr index 3c0941b741c..4a433de8933 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr @@ -1,6 +1,9 @@ +mod historic_commitment_oracle; + // A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests. contract StatefulTest { use dep::std::option::Option; + use dep::std::merkle::compute_merkle_root; use dep::value_note::{ balance_utils, utils::{increment, decrement}, @@ -17,6 +20,9 @@ contract StatefulTest { FieldSerializationMethods, FIELD_SERIALIZED_LEN, }, }; + use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; + use crate::historic_commitment_oracle::{get_private_data_sibling_witness, PrivateDataMembershipWitness}; + use dep::aztec::note::utils::compute_note_hash_for_read_or_nullify; struct Storage { notes: Map>, @@ -82,6 +88,28 @@ contract StatefulTest { increment(recipient_notes, amount, recipient); } + unconstrained fn get_path( + note: ValueNote + ) -> [Field; PRIVATE_DATA_TREE_HEIGHT] { + // compute the commitment + let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); + let witness = get_private_data_sibling_witness(commitment); + witness.path + } + + unconstrained fn get_commitment( + note: ValueNote + ) -> Field { + compute_note_hash_for_read_or_nullify(ValueNoteMethods, note) + } + + unconstrained fn get_root(note: ValueNote) -> Field { + // compute the commitment + let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); + let witness = get_private_data_sibling_witness(commitment); + compute_merkle_root(commitment, witness.index, witness.path) + } + unconstrained fn summed_values( owner: Field, ) -> Field { diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 7aae22c6b4d..932653238ff 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -99,11 +99,15 @@ export class SimulatorOracle implements DBOracle { /** * Gets the index of a commitment in the private data tree. - * @param commitment - The commitment. + * @param leafValue - The commitment buffer. * @returns - The index of the commitment. Undefined if it does not exist in the tree. */ - async getCommitmentIndex(commitment: Fr) { - return await this.dataTreeProvider.findCommitmentIndex(commitment.toBuffer()); + async findCommitmentIndex(leafValue: Buffer) { + return await this.dataTreeProvider.findCommitmentIndex(leafValue); + } + + async getDataTreePath(leafIndex: bigint) { + return await this.dataTreeProvider.getDataTreePath(leafIndex); } /** diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 1296dcae50a..8ad703e6b81 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -6,7 +6,7 @@ import { PublicStateDB, } from '@aztec/acir-simulator'; import { AztecAddress, CircuitsWasm, EthAddress, Fr, FunctionSelector, HistoricBlockData } from '@aztec/circuits.js'; -import { ContractDataSource, L1ToL2MessageSource, MerkleTreeId } from '@aztec/types'; +import { ContractDataSource, L1ToL2MessageSource, MerkleTreeId, SiblingPath } from '@aztec/types'; import { MerkleTreeOperations, computePublicDataTreeLeafIndex } from '@aztec/world-state'; /** @@ -98,7 +98,11 @@ export class WorldStateDB implements CommitmentsDB { }; } - public async getCommitmentIndex(commitment: Fr): Promise { - return await this.db.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, commitment.toBuffer()); + public async findCommitmentIndex(commitment: Buffer): Promise { + return await this.db.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, commitment); + } + + public async getDataTreePath(leafIndex: bigint): Promise> { + return await this.db.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, leafIndex); } } From 630d3c928e8e83cd3736c9044cf29004c62d030f Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 28 Sep 2023 13:37:03 +0000 Subject: [PATCH 02/10] fix: pedersen hash vs commit --- .circleci/config.yml | 12 +++++ .../crypto/pedersen_hash/pedersen_lookup.cpp | 5 +- .../src/e2e_stateful_test_contract.test.ts | 50 ++++++++----------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b05b90cf172..c145ea14f16 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -553,6 +553,18 @@ jobs: name: "Test" command: cond_run_script end-to-end ./scripts/run_tests_local e2e_deploy_contract.test.ts + e2e-stateful-test-contract: + machine: + image: ubuntu-2204:2023.07.2 + resource_class: large + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: cond_run_script end-to-end ./scripts/run_tests_local e2e_stateful_test_contract.test.ts + + e2e-lending-contract: machine: image: ubuntu-2204:2023.07.2 diff --git a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp index 3c1cc5eb835..0eda1c6ece7 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/pedersen_lookup.cpp @@ -173,12 +173,13 @@ grumpkin::fq hash_multiple(const std::vector& inputs, const size_t const size_t num_inputs = inputs.size(); grumpkin::fq result = (pedersen_iv_table[hash_index]).x; - for (size_t i = 0; i < num_inputs; i++) { + result = hash_pair(result, num_inputs); + for (size_t i = 0; i < num_inputs - 1; i++) { result = hash_pair(result, inputs[i]); } auto final_result = - grumpkin::g1::affine_element(hash_single(result, false) + hash_single(grumpkin::fq(num_inputs), true)); + grumpkin::g1::affine_element(hash_single(result, false) + hash_single(inputs[num_inputs - 1], true)); return final_result.x; } diff --git a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts index 63c33b03e57..74927ba26d6 100644 --- a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -1,9 +1,8 @@ /* eslint-disable camelcase */ -import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecNodeService } from '@aztec/aztec-node'; import { CheatCodes, Fr, Wallet } from '@aztec/aztec.js'; import { CircuitsWasm, CompleteAddress } from '@aztec/circuits.js'; import { pedersenHashInputs, pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; -import { DebugLogger } from '@aztec/foundation/log'; import { StatefulTestContract } from '@aztec/noir-contracts/types'; import { PXE } from '@aztec/types'; @@ -13,7 +12,6 @@ describe('e2e_deploy_contract', () => { let aztecNode: AztecNodeService | undefined; let pxe: PXE; let accounts: CompleteAddress[]; - let logger: DebugLogger; let wallet: Wallet; let teardown: () => Promise; let cheatCodes: CheatCodes; @@ -21,7 +19,7 @@ describe('e2e_deploy_contract', () => { let statefulContract: StatefulTestContract; beforeEach(async () => { - ({ teardown, aztecNode, pxe, accounts, logger, wallet, cheatCodes } = await setup()); + ({ teardown, aztecNode, pxe, accounts, wallet, cheatCodes } = await setup()); statefulContract = await StatefulTestContract.deploy(wallet, accounts[0].address, 1).send().deployed(); }, 100_000); @@ -33,8 +31,6 @@ describe('e2e_deploy_contract', () => { } const owner = accounts[0].address; - console.log(`Historic: ${(await aztecNode.getHistoricBlockData()).privateDataTreeRoot.toBigInt()}`); - const tx = statefulContract.methods.create_note(owner, 99n).send(); const receipt = await tx.wait(); @@ -55,47 +51,41 @@ describe('e2e_deploy_contract', () => { }, }; - const commitment = new Fr(await statefulContract.methods.get_commitment(valueNote).view()); - console.log(`Commitment: ${commitment}`); - - const index = await aztecNode.findCommitmentIndex(commitment.toBuffer()); - console.log(`Index: ${index}`); - const circuitsWasm = await CircuitsWasm.get(); - // pedersenPlookupCommitInputs - // const h = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenPlookupCommitInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); - const h = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenHashInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); + + // These functions should be the same after modifications to underlying. + const h1 = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenPlookupCommitInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); // Matches noir + const h2 = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenHashInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); // Matches kernel and circuits + const commitment = new Fr(await statefulContract.methods.get_commitment(valueNote).view()); + const index = await aztecNode.findCommitmentIndex(commitment.toBuffer()); const path = await statefulContract.methods.get_path(valueNote).view(); - console.log(`Path: `, path); + const root = await statefulContract.methods.get_root(valueNote).view(); // computes root in noir using path - const historic = await aztecNode.getHistoricBlockData(); - const root = await statefulContract.methods.get_root(valueNote).view(); - console.log(`Historic: ${historic.privateDataTreeRoot.toBigInt()}, our computed: ${root}`); - - // Computing the root from path, using the `pedersenHashInputs` - // crypto::pedersen_hash::lookup::hash_multiple function. - // Noir is using: - // crypto::pedersen_commitment::lookup::compress_native - const rootFromPath = (leaf: Fr, index: bigint, path: any[]) => { + const rootFromPath = (leaf: Fr, index: bigint, path: any[], hash: (a: Fr, b: Fr) => Fr) => { const temps: bigint[] = []; let node = leaf; let i = index; for (const sibling of path) { if (i % 2n === 0n) { - node = h(node, new Fr(sibling)); + node = hash(node, new Fr(sibling)); } else { - node = h(new Fr(sibling), node); + node = hash(new Fr(sibling), node); } temps.push(node.toBigInt()); i /= 2n; } - console.log(temps); return node; }; - const myRoot = rootFromPath(commitment, index ?? 0n, path.slice()); - console.log(`Historic: ${historic.privateDataTreeRoot.toBigInt()}, myroot: ${myRoot.toBigInt()}`); + const myRootPedersenCommit = rootFromPath(commitment, index ?? 0n, path.slice(), h1); + const myRootPedersenHash = rootFromPath(commitment, index ?? 0n, path.slice(), h2); + + const historic = await aztecNode.getHistoricBlockData(); + + expect(historic.privateDataTreeRoot).toEqual(myRootPedersenCommit); + expect(historic.privateDataTreeRoot).toEqual(myRootPedersenHash); + expect(historic.privateDataTreeRoot).toEqual(new Fr(root)); }); }); From c59a6beb2b520848266f960fec743b692c122bef Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 28 Sep 2023 15:49:57 +0000 Subject: [PATCH 03/10] feat: check note is in history --- .../src/client/client_execution_context.ts | 2 +- .../src/e2e_stateful_test_contract.test.ts | 18 +++++++-- .../src/almost_value_note.nr | 38 +++++++++++++++++++ .../stateful_test_contract/src/main.nr | 14 +++++++ 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/almost_value_note.nr diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index f8b16a289df..78527fbe266 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -349,7 +349,7 @@ export class ClientExecutionContext extends ViewDataOracle { */ async callPrivateFunction(targetContractAddress: AztecAddress, functionSelector: FunctionSelector, argsHash: Fr) { this.log( - `Calling private function ${this.contractAddress}:${functionSelector} from ${this.callContext.storageContractAddress}`, + `Calling private function ${targetContractAddress}:${functionSelector} from ${this.callContext.storageContractAddress}`, ); const targetAbi = await this.db.getFunctionABI(targetContractAddress, functionSelector); diff --git a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts index 74927ba26d6..148a7bfafaa 100644 --- a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -1,7 +1,7 @@ /* eslint-disable camelcase */ import { AztecNodeService } from '@aztec/aztec-node'; -import { CheatCodes, Fr, Wallet } from '@aztec/aztec.js'; -import { CircuitsWasm, CompleteAddress } from '@aztec/circuits.js'; +import { CheatCodes, Fr, TxStatus, Wallet } from '@aztec/aztec.js'; +import { CircuitsWasm, CompleteAddress, FunctionSelector } from '@aztec/circuits.js'; import { pedersenHashInputs, pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; import { StatefulTestContract } from '@aztec/noir-contracts/types'; import { PXE } from '@aztec/types'; @@ -52,7 +52,7 @@ describe('e2e_deploy_contract', () => { }; const circuitsWasm = await CircuitsWasm.get(); - + // These functions should be the same after modifications to underlying. const h1 = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenPlookupCommitInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); // Matches noir const h2 = (a: Fr, b: Fr) => Fr.fromBuffer(pedersenHashInputs(circuitsWasm, [a.toBuffer(), b.toBuffer()])); // Matches kernel and circuits @@ -87,5 +87,15 @@ describe('e2e_deploy_contract', () => { expect(historic.privateDataTreeRoot).toEqual(myRootPedersenCommit); expect(historic.privateDataTreeRoot).toEqual(myRootPedersenHash); expect(historic.privateDataTreeRoot).toEqual(new Fr(root)); - }); + + const isIncludedTx = statefulContract.methods.is_included_in_history(valueNote).send(); + const isIncludedReceipt = await isIncludedTx.wait(); + expect(isIncludedReceipt.status).toEqual(TxStatus.MINED); + + const badValueNote = { ...valueNote, owner: 100n }; + const badCommitment = new Fr(await statefulContract.methods.get_commitment(badValueNote).view()); + await expect(statefulContract.methods.is_included_in_history(badValueNote).simulate()).rejects.toThrowError( + `Commitment ${badCommitment} not found in private data tree`, + ); + }, 30_000); }); diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/almost_value_note.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/almost_value_note.nr new file mode 100644 index 00000000000..c18977e4b70 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/almost_value_note.nr @@ -0,0 +1,38 @@ + +use dep::value_note::value_note::ValueNote; +use dep::aztec::note::note_header::NoteHeader; + +struct AlmostValueNote { + value: Field, + owner: Field, + randomness: Field, + header: NoteHeader, +} + +impl AlmostValueNote { + fn serialize(self) -> [Field; 6] { + [self.value, self.owner, self.randomness, self.header.contract_address, self.header.nonce, self.header.storage_slot] + } + + fn deserialize(serialized: [Field; 6]) -> Self { + AlmostValueNote { + value: serialized[0], + owner: serialized[1], + randomness: serialized[2], + header: NoteHeader { + contract_address: serialized[3], + nonce: serialized[4], + storage_slot: serialized[5], + }, + } + } + + fn to_value_note(self) -> ValueNote { + ValueNote { + value: self.value, + owner: self.owner, + randomness: self.randomness, + header: self.header, + } + } +} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr index 4a433de8933..57bbd25ee76 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr @@ -1,4 +1,5 @@ mod historic_commitment_oracle; +mod almost_value_note; // A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests. contract StatefulTest { @@ -23,6 +24,7 @@ contract StatefulTest { use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; use crate::historic_commitment_oracle::{get_private_data_sibling_witness, PrivateDataMembershipWitness}; use dep::aztec::note::utils::compute_note_hash_for_read_or_nullify; + use crate::almost_value_note::AlmostValueNote; struct Storage { notes: Map>, @@ -88,6 +90,18 @@ contract StatefulTest { increment(recipient_notes, amount, recipient); } + // We cannot use valueNote directly because of its serialize here. Unconstrained are different. + #[aztec(private)] + fn is_included_in_history( + note: AlmostValueNote + ) { + let note = note.to_value_note(); + let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); + let witness = get_private_data_sibling_witness(commitment); + let root = compute_merkle_root(commitment, witness.index, witness.path); + assert(context.block_data.private_data_tree_root == root, "Note is not included in history"); + } + unconstrained fn get_path( note: ValueNote ) -> [Field; PRIVATE_DATA_TREE_HEIGHT] { From 422bc4a43dc5d1cca66ca2f44c0c92555efefe9d Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 28 Sep 2023 16:26:46 +0000 Subject: [PATCH 04/10] fix: use the new pedersen to validate l1l2 messages --- .circleci/config.yml | 2 ++ yarn-project/aztec-nr/aztec/src/messaging.nr | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c145ea14f16..798bf924270 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1200,6 +1200,7 @@ workflows: - e2e-2-pxes: *e2e_test - e2e-deploy-contract: *e2e_test + - e2e-stateful-test-contract: e2e_test - e2e-lending-contract: *e2e_test - e2e-token-contract: *e2e_test - e2e-private-airdrop: *e2e_test @@ -1237,6 +1238,7 @@ workflows: requires: - e2e-2-pxes - e2e-deploy-contract + - e2e-stateful-test-contract - e2e-lending-contract - e2e-token-contract - e2e-private-airdrop diff --git a/yarn-project/aztec-nr/aztec/src/messaging.nr b/yarn-project/aztec-nr/aztec/src/messaging.nr index 8f41c29c943..caf8932ea32 100644 --- a/yarn-project/aztec-nr/aztec/src/messaging.nr +++ b/yarn-project/aztec-nr/aztec/src/messaging.nr @@ -5,7 +5,7 @@ use l1_to_l2_message_getter_data::make_l1_to_l2_message_getter_data; use crate::abi::PublicContextInputs; use crate::oracle::get_l1_to_l2_message::get_l1_to_l2_message_call; - +use dep::std::merkle::compute_merkle_root; // Returns the nullifier for the message fn process_l1_to_l2_message(l1_to_l2_root: Field, storage_contract_address: Field, msg_key: Field, content: Field, secret: Field) -> Field{ @@ -13,8 +13,13 @@ fn process_l1_to_l2_message(l1_to_l2_root: Field, storage_contract_address: Fiel let returned_message = get_l1_to_l2_message_call(msg_key); let l1_to_l2_message_data = make_l1_to_l2_message_getter_data(returned_message, 0, secret); - // Check tree roots against the inputs - assert(l1_to_l2_message_data.root == l1_to_l2_root); + // Commitment should equal the msg_key if the message exsits and oracle is honest. + let commitment = l1_to_l2_message_data.message.message_hash(); + assert(commitment == msg_key, "Data don't match the message key"); + + // Validate that the commitment is indeed in the l1 to l2 message tree. + let root = compute_merkle_root(commitment, l1_to_l2_message_data.leaf_index, l1_to_l2_message_data.sibling_path); + assert(root == l1_to_l2_root, "Invalid root"); // Validate this is the target contract assert(l1_to_l2_message_data.message.recipient == storage_contract_address); From 73e0582e348382da4e0ad03c7a02b404dd92eb9a Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 28 Sep 2023 16:34:48 +0000 Subject: [PATCH 05/10] chore: fix config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 798bf924270..146046b0d42 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1200,7 +1200,7 @@ workflows: - e2e-2-pxes: *e2e_test - e2e-deploy-contract: *e2e_test - - e2e-stateful-test-contract: e2e_test + - e2e-stateful-test-contract: *e2e_test - e2e-lending-contract: *e2e_test - e2e-token-contract: *e2e_test - e2e-private-airdrop: *e2e_test From e22dd2e6e524197c072d6bd8668bbe769b468d69 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 29 Sep 2023 10:51:13 +0000 Subject: [PATCH 06/10] chore: fix oracle naming --- yarn-project/acir-simulator/src/acvm/oracle/oracle.ts | 4 ++-- .../acir-simulator/src/acvm/oracle/typed_oracle.ts | 2 +- .../acir-simulator/src/client/view_data_oracle.ts | 9 +++++---- yarn-project/aztec-nr/aztec/src/messaging.nr | 8 ++++---- .../end-to-end/src/e2e_stateful_test_contract.test.ts | 4 ++-- .../src/historic_commitment_oracle.nr | 8 ++++---- .../src/contracts/stateful_test_contract/src/main.nr | 8 ++++---- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 2e3d977aee6..7c084b30681 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -130,8 +130,8 @@ export class Oracle { return toAcvmL1ToL2MessageLoadOracleInputs(message, root); } - async getDataTreePath([commitment]: ACVMField[]): Promise { - return (await this.typedOracle.getDataTreePath(fromACVMField(commitment))).map(toACVMField); + async getMembershipWitness([leafValue]: ACVMField[]): Promise { + return (await this.typedOracle.getMembershipWitness(fromACVMField(leafValue))).map(toACVMField); } async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise { 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 2529a48a74f..28a6cfe350b 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -113,7 +113,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getDataTreePath(_commitment: Fr): Promise { + getMembershipWitness(_commitment: Fr): Promise { throw new Error('Not available.'); } 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 9d4d0fd266c..eff5f947a4a 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -130,13 +130,14 @@ export class ViewDataOracle extends TypedOracle { /** * Fetches the index and data tree path for a commitment - * @param commitment - The commitment + * @param leafValue - The commitment * @returns The index and data tree path */ - public async getDataTreePath(commitment: Fr): Promise { - const index = await this.db.findCommitmentIndex(commitment.toBuffer()); + public async getMembershipWitness(leafValue: Fr): Promise { + // @todo @lherskind #2572 + const index = await this.db.findCommitmentIndex(leafValue.toBuffer()); if (!index) { - throw new Error(`Commitment ${commitment} not found in private data tree`); + throw new Error(`Leaf value: ${leafValue} not found in private data tree`); } const path = await this.db.getDataTreePath(index); return [new Fr(index), ...path.toFieldArray()]; diff --git a/yarn-project/aztec-nr/aztec/src/messaging.nr b/yarn-project/aztec-nr/aztec/src/messaging.nr index caf8932ea32..b5723cdcc13 100644 --- a/yarn-project/aztec-nr/aztec/src/messaging.nr +++ b/yarn-project/aztec-nr/aztec/src/messaging.nr @@ -13,12 +13,12 @@ fn process_l1_to_l2_message(l1_to_l2_root: Field, storage_contract_address: Fiel let returned_message = get_l1_to_l2_message_call(msg_key); let l1_to_l2_message_data = make_l1_to_l2_message_getter_data(returned_message, 0, secret); - // Commitment should equal the msg_key if the message exsits and oracle is honest. - let commitment = l1_to_l2_message_data.message.message_hash(); - assert(commitment == msg_key, "Data don't match the message key"); + // leaf should equal the msg_key if the message exsits and oracle is honest. + let leaf = l1_to_l2_message_data.message.message_hash(); + assert(leaf == msg_key, "Data don't match the message key"); // Validate that the commitment is indeed in the l1 to l2 message tree. - let root = compute_merkle_root(commitment, l1_to_l2_message_data.leaf_index, l1_to_l2_message_data.sibling_path); + let root = compute_merkle_root(leaf, l1_to_l2_message_data.leaf_index, l1_to_l2_message_data.sibling_path); assert(root == l1_to_l2_root, "Invalid root"); // Validate this is the target contract diff --git a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts index 148a7bfafaa..6b5aa179e29 100644 --- a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -93,9 +93,9 @@ describe('e2e_deploy_contract', () => { expect(isIncludedReceipt.status).toEqual(TxStatus.MINED); const badValueNote = { ...valueNote, owner: 100n }; - const badCommitment = new Fr(await statefulContract.methods.get_commitment(badValueNote).view()); + const badLeafValue = new Fr(await statefulContract.methods.get_commitment(badValueNote).view()); await expect(statefulContract.methods.is_included_in_history(badValueNote).simulate()).rejects.toThrowError( - `Commitment ${badCommitment} not found in private data tree`, + `Leaf value: ${badLeafValue} not found in private data tree`, ); }, 30_000); }); diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr index 4287ecfb33c..b6694bbd46d 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr @@ -8,12 +8,12 @@ struct PrivateDataMembershipWitness { path: [Field; PRIVATE_DATA_TREE_HEIGHT], } -#[oracle(getDataTreePath)] -fn get_private_data_sibling_witness_oracle(_commitment: Field) -> [Field; PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN] {} +#[oracle(getMembershipWitness)] +fn get_private_data_membership_witness_oracle(_commitment: Field) -> [Field; PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN] {} -unconstrained fn get_private_data_sibling_witness(commitment: Field) -> PrivateDataMembershipWitness { +unconstrained fn get_private_data_membership_witness(commitment: Field) -> PrivateDataMembershipWitness { assert(PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN == PRIVATE_DATA_TREE_HEIGHT + 1); - let fields = get_private_data_sibling_witness_oracle(commitment); + let fields = get_private_data_membership_witness_oracle(commitment); PrivateDataMembershipWitness { index: fields[0], path: arr_copy_slice(fields, [0; PRIVATE_DATA_TREE_HEIGHT], 1) diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr index 57bbd25ee76..56668643229 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr @@ -22,7 +22,7 @@ contract StatefulTest { }, }; use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; - use crate::historic_commitment_oracle::{get_private_data_sibling_witness, PrivateDataMembershipWitness}; + use crate::historic_commitment_oracle::{get_private_data_membership_witness, PrivateDataMembershipWitness}; use dep::aztec::note::utils::compute_note_hash_for_read_or_nullify; use crate::almost_value_note::AlmostValueNote; @@ -97,7 +97,7 @@ contract StatefulTest { ) { let note = note.to_value_note(); let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); - let witness = get_private_data_sibling_witness(commitment); + let witness = get_private_data_membership_witness(commitment); let root = compute_merkle_root(commitment, witness.index, witness.path); assert(context.block_data.private_data_tree_root == root, "Note is not included in history"); } @@ -107,7 +107,7 @@ contract StatefulTest { ) -> [Field; PRIVATE_DATA_TREE_HEIGHT] { // compute the commitment let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); - let witness = get_private_data_sibling_witness(commitment); + let witness = get_private_data_membership_witness(commitment); witness.path } @@ -120,7 +120,7 @@ contract StatefulTest { unconstrained fn get_root(note: ValueNote) -> Field { // compute the commitment let commitment = compute_note_hash_for_read_or_nullify(ValueNoteMethods, note); - let witness = get_private_data_sibling_witness(commitment); + let witness = get_private_data_membership_witness(commitment); compute_merkle_root(commitment, witness.index, witness.path) } From 445b628474540f526732fe7f4a2b33aaa614a0f0 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 29 Sep 2023 10:52:59 +0000 Subject: [PATCH 07/10] chore: variable naming --- yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28a6cfe350b..228775e6d2d 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -113,7 +113,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getMembershipWitness(_commitment: Fr): Promise { + getMembershipWitness(_leafValue: Fr): Promise { throw new Error('Not available.'); } From f1b3d2b7b6a8129ed875605f94435817743f7e9b Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 29 Sep 2023 13:43:36 +0000 Subject: [PATCH 08/10] feat: foundation for general historic data oracle --- .../acir-simulator/src/acvm/oracle/oracle.ts | 6 ++++-- .../src/acvm/oracle/typed_oracle.ts | 4 ++-- .../acir-simulator/src/client/db_oracle.ts | 16 ++++++++++++++ .../src/client/view_data_oracle.ts | 20 +++++++++--------- .../src/e2e_stateful_test_contract.test.ts | 6 +++--- ...racle.nr => historic_membership_oracle.nr} | 4 ++-- .../stateful_test_contract/src/main.nr | 4 ++-- .../pxe/src/simulator_oracle/index.ts | 21 ++++++++++++++++++- 8 files changed, 59 insertions(+), 22 deletions(-) rename yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/{historic_commitment_oracle.nr => historic_membership_oracle.nr} (74%) diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 7c084b30681..334e2ca2ab8 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -4,6 +4,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { padArrayEnd } from '@aztec/foundation/collection'; import { Fr, Point } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import { MerkleTreeId } from '@aztec/types'; import { ACVMField } from '../acvm.js'; import { convertACVMFieldToBuffer, fromACVMField } from '../deserialize.js'; @@ -130,8 +131,9 @@ export class Oracle { return toAcvmL1ToL2MessageLoadOracleInputs(message, root); } - async getMembershipWitness([leafValue]: ACVMField[]): Promise { - return (await this.typedOracle.getMembershipWitness(fromACVMField(leafValue))).map(toACVMField); + async getMembershipWitness([treeId]: ACVMField[], [leafValue]: ACVMField[]): Promise { + const merkleTreeId: MerkleTreeId = Number(fromACVMField(treeId).toBigInt()); + return (await this.typedOracle.getMembershipWitness(merkleTreeId, fromACVMField(leafValue))).map(toACVMField); } async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise { 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 228775e6d2d..2a59677dddb 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -3,7 +3,7 @@ import { FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; -import { CompleteAddress, PublicKey } from '@aztec/types'; +import { CompleteAddress, MerkleTreeId, PublicKey } from '@aztec/types'; /** * Information about a note needed during execution. @@ -113,7 +113,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getMembershipWitness(_leafValue: Fr): Promise { + getMembershipWitness(_treeId: MerkleTreeId, _leafValue: Fr): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index a2fb45431ae..3e176243fd8 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -3,6 +3,7 @@ import { FunctionAbi, FunctionDebugMetadata, FunctionSelector } from '@aztec/fou import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { MerkleTreeId } from '@aztec/types'; import { NoteData } from '../acvm/index.js'; import { CommitmentsDB } from '../public/index.js'; @@ -84,4 +85,19 @@ export interface DBOracle extends CommitmentsDB { * @returns A Promise that resolves to a HistoricBlockData object. */ getHistoricBlockData(): Promise; + + /** + * Fetch the index of the leaf in the respective tree + * @param treeId - The id of the tree to search. + * @param leafValue - The leaf value buffer. + * @returns - The index of the leaf. Undefined if it does not exist in the tree. + */ + findLeafIndex(treeId: MerkleTreeId, leafValue: Buffer): Promise; + + /** + * Fetch the sibling path of the leaf in the respective tree + * @param treeId - The id of the tree to search. + * @param leafIndex - The index of the leaf. + */ + getSiblingPath(treeId: MerkleTreeId, leafIndex: bigint): Promise; } 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 eff5f947a4a..e244bf982ef 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -3,7 +3,7 @@ import { computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { AuthWitness, AztecNode, CompleteAddress } from '@aztec/types'; +import { AuthWitness, AztecNode, CompleteAddress, MerkleTreeId } from '@aztec/types'; import { NoteData, TypedOracle } from '../acvm/index.js'; import { DBOracle } from './db_oracle.js'; @@ -129,18 +129,18 @@ export class ViewDataOracle extends TypedOracle { } /** - * Fetches the index and data tree path for a commitment - * @param leafValue - The commitment - * @returns The index and data tree path + * Fetches the index and sibling path for a leaf value + * @param treeId - The tree id + * @param leafValue - The leaf value + * @returns The index and sibling path concatenated [index, sibling_path] */ - public async getMembershipWitness(leafValue: Fr): Promise { - // @todo @lherskind #2572 - const index = await this.db.findCommitmentIndex(leafValue.toBuffer()); + public async getMembershipWitness(treeId: MerkleTreeId, leafValue: Fr): Promise { + const index = await this.db.findLeafIndex(treeId, leafValue.toBuffer()); if (!index) { - throw new Error(`Leaf value: ${leafValue} not found in private data tree`); + throw new Error(`Leaf value: ${leafValue} not found in tree ${treeId}`); } - const path = await this.db.getDataTreePath(index); - return [new Fr(index), ...path.toFieldArray()]; + const siblingPath = await this.db.getSiblingPath(treeId, index); + return [new Fr(index), ...siblingPath]; } /** diff --git a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts index 6b5aa179e29..b241f7e7808 100644 --- a/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -4,7 +4,7 @@ import { CheatCodes, Fr, TxStatus, Wallet } from '@aztec/aztec.js'; import { CircuitsWasm, CompleteAddress, FunctionSelector } from '@aztec/circuits.js'; import { pedersenHashInputs, pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg'; import { StatefulTestContract } from '@aztec/noir-contracts/types'; -import { PXE } from '@aztec/types'; +import { MerkleTreeId, PXE } from '@aztec/types'; import { setup } from './fixtures/utils.js'; @@ -25,7 +25,7 @@ describe('e2e_deploy_contract', () => { afterEach(() => teardown()); - it('Try to get a witness', async () => { + it('Check commitment inclusion in historic data', async () => { if (!aztecNode) { throw new Error('No aztec node'); } @@ -95,7 +95,7 @@ describe('e2e_deploy_contract', () => { const badValueNote = { ...valueNote, owner: 100n }; const badLeafValue = new Fr(await statefulContract.methods.get_commitment(badValueNote).view()); await expect(statefulContract.methods.is_included_in_history(badValueNote).simulate()).rejects.toThrowError( - `Leaf value: ${badLeafValue} not found in private data tree`, + `Leaf value: ${badLeafValue} not found in tree ${MerkleTreeId.PRIVATE_DATA_TREE}`, ); }, 30_000); }); diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_membership_oracle.nr similarity index 74% rename from yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr rename to yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_membership_oracle.nr index b6694bbd46d..5c61ea5831c 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_commitment_oracle.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_membership_oracle.nr @@ -9,11 +9,11 @@ struct PrivateDataMembershipWitness { } #[oracle(getMembershipWitness)] -fn get_private_data_membership_witness_oracle(_commitment: Field) -> [Field; PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN] {} +fn get_private_data_membership_witness_oracle(_treeId: Field, _commitment: Field) -> [Field; PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN] {} unconstrained fn get_private_data_membership_witness(commitment: Field) -> PrivateDataMembershipWitness { assert(PRIVATE_DATA_MEMBERSHIP_WITNESS_LEN == PRIVATE_DATA_TREE_HEIGHT + 1); - let fields = get_private_data_membership_witness_oracle(commitment); + let fields = get_private_data_membership_witness_oracle(2, commitment); PrivateDataMembershipWitness { index: fields[0], path: arr_copy_slice(fields, [0; PRIVATE_DATA_TREE_HEIGHT], 1) diff --git a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr index 56668643229..74e2208cb8e 100644 --- a/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/main.nr @@ -1,4 +1,4 @@ -mod historic_commitment_oracle; +mod historic_membership_oracle; mod almost_value_note; // A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests. @@ -22,7 +22,7 @@ contract StatefulTest { }, }; use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; - use crate::historic_commitment_oracle::{get_private_data_membership_witness, PrivateDataMembershipWitness}; + use crate::historic_membership_oracle::{get_private_data_membership_witness, PrivateDataMembershipWitness}; use dep::aztec::note::utils::compute_note_hash_for_read_or_nullify; use crate::almost_value_note::AlmostValueNote; diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 932653238ff..b7c9da7c065 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -9,7 +9,7 @@ import { HistoricBlockData, PublicKey, } from '@aztec/circuits.js'; -import { DataCommitmentProvider, KeyStore, L1ToL2MessageProvider } from '@aztec/types'; +import { DataCommitmentProvider, KeyStore, L1ToL2MessageProvider, MerkleTreeId } from '@aztec/types'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; import { Database } from '../database/index.js'; @@ -110,6 +110,25 @@ export class SimulatorOracle implements DBOracle { return await this.dataTreeProvider.getDataTreePath(leafIndex); } + public async findLeafIndex(treeId: MerkleTreeId, leafValue: Buffer): Promise { + switch (treeId) { + case MerkleTreeId.PRIVATE_DATA_TREE: + return await this.dataTreeProvider.findCommitmentIndex(leafValue); + default: + throw new Error('Not implemented'); + } + } + + public async getSiblingPath(treeId: MerkleTreeId, leafIndex: bigint): Promise { + // @todo This is doing a nasty workaround as http_rpc_client was not happy about a generic `getSiblingPath` function being exposed. + switch (treeId) { + case MerkleTreeId.PRIVATE_DATA_TREE: + return (await this.dataTreeProvider.getDataTreePath(leafIndex)).toFieldArray(); + default: + throw new Error('Not implemented'); + } + } + /** * Retrieve the databases view of the Historic Block Data object. * This structure is fed into the circuits simulator and is used to prove against certain historic roots. From d651e895670679238d197181f054696e5226abd6 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 29 Sep 2023 14:01:41 +0000 Subject: [PATCH 09/10] chore: house-keeping --- yarn-project/acir-simulator/src/public/db.ts | 9 ++++++++- yarn-project/pxe/src/simulator_oracle/index.ts | 6 +----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/yarn-project/acir-simulator/src/public/db.ts b/yarn-project/acir-simulator/src/public/db.ts index 59819601b74..f09174720c8 100644 --- a/yarn-project/acir-simulator/src/public/db.ts +++ b/yarn-project/acir-simulator/src/public/db.ts @@ -56,7 +56,7 @@ export interface PublicContractsDB { } /** Database interface for providing access to commitment tree and l1 to l2 messages tree (append only data trees). */ -export interface CommitmentsDB extends DataCommitmentProvider { +export interface CommitmentsDB { /** * Gets a confirmed L1 to L2 message for the given message key. * TODO(Maddiaa): Can be combined with aztec-node method that does the same thing. @@ -64,4 +64,11 @@ export interface CommitmentsDB extends DataCommitmentProvider { * @returns - The l1 to l2 message object */ getL1ToL2Message(msgKey: Fr): Promise; + + /** + * Find the index of the given commitment. + * @param leafValue - The value to search for. + * @returns The index of the given leaf of undefined if not found. + */ + findCommitmentIndex(leafValue: Buffer): Promise; } diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index b7c9da7c065..ac0b96a9305 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -103,11 +103,7 @@ export class SimulatorOracle implements DBOracle { * @returns - The index of the commitment. Undefined if it does not exist in the tree. */ async findCommitmentIndex(leafValue: Buffer) { - return await this.dataTreeProvider.findCommitmentIndex(leafValue); - } - - async getDataTreePath(leafIndex: bigint) { - return await this.dataTreeProvider.getDataTreePath(leafIndex); + return await this.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, leafValue); } public async findLeafIndex(treeId: MerkleTreeId, leafValue: Buffer): Promise { From 843a5f952779f39f8f16dd722814815d61b4be02 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 29 Sep 2023 14:03:20 +0000 Subject: [PATCH 10/10] =?UTF-8?q?chore:=20=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yarn-project/acir-simulator/src/public/db.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/public/db.ts b/yarn-project/acir-simulator/src/public/db.ts index f09174720c8..df79cd7edc5 100644 --- a/yarn-project/acir-simulator/src/public/db.ts +++ b/yarn-project/acir-simulator/src/public/db.ts @@ -1,7 +1,6 @@ import { EthAddress, FunctionSelector } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; -import { DataCommitmentProvider } from '@aztec/types'; import { MessageLoadOracleInputs } from '../acvm/index.js';