diff --git a/.circleci/config.yml b/.circleci/config.yml index b05b90cf172..146046b0d42 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 @@ -1188,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 @@ -1225,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/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/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index e587bb7db6c..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,6 +131,11 @@ export class Oracle { return toAcvmL1ToL2MessageLoadOracleInputs(message, root); } + 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 { 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..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,6 +113,10 @@ export abstract class TypedOracle { throw new Error('Not available.'); } + getMembershipWitness(_treeId: MerkleTreeId, _leafValue: 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..78527fbe266 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); @@ -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/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/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..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'; @@ -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,21 @@ export class ViewDataOracle extends TypedOracle { return { ...message, root: this.historicBlockData.l1ToL2MessagesTreeRoot }; } + /** + * 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(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 tree ${treeId}`); + } + const siblingPath = await this.db.getSiblingPath(treeId, index); + return [new Fr(index), ...siblingPath]; + } + /** * 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..df79cd7edc5 100644 --- a/yarn-project/acir-simulator/src/public/db.ts +++ b/yarn-project/acir-simulator/src/public/db.ts @@ -65,9 +65,9 @@ export interface CommitmentsDB { 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. + * 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. */ - getCommitmentIndex(commitment: Fr): Promise; + findCommitmentIndex(leafValue: Buffer): 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/aztec-nr/aztec/src/messaging.nr b/yarn-project/aztec-nr/aztec/src/messaging.nr index 8f41c29c943..b5723cdcc13 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); + // 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(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 assert(l1_to_l2_message_data.message.recipient == storage_contract_address); 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..b241f7e7808 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_stateful_test_contract.test.ts @@ -0,0 +1,101 @@ +/* eslint-disable camelcase */ +import { AztecNodeService } from '@aztec/aztec-node'; +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 { MerkleTreeId, 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 wallet: Wallet; + let teardown: () => Promise; + let cheatCodes: CheatCodes; + + let statefulContract: StatefulTestContract; + + beforeEach(async () => { + ({ teardown, aztecNode, pxe, accounts, wallet, cheatCodes } = await setup()); + statefulContract = await StatefulTestContract.deploy(wallet, accounts[0].address, 1).send().deployed(); + }, 100_000); + + afterEach(() => teardown()); + + it('Check commitment inclusion in historic data', async () => { + if (!aztecNode) { + throw new Error('No aztec node'); + } + const owner = accounts[0].address; + + 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 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 + + 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(); + const root = await statefulContract.methods.get_root(valueNote).view(); // computes root in noir using path + + 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 = hash(node, new Fr(sibling)); + } else { + node = hash(new Fr(sibling), node); + } + + temps.push(node.toBigInt()); + i /= 2n; + } + return node; + }; + + 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)); + + 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 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 tree ${MerkleTreeId.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/historic_membership_oracle.nr b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_membership_oracle.nr new file mode 100644 index 00000000000..5c61ea5831c --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/stateful_test_contract/src/historic_membership_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(getMembershipWitness)] +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(2, 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..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,6 +1,10 @@ +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. 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 +21,10 @@ contract StatefulTest { FieldSerializationMethods, FIELD_SERIALIZED_LEN, }, }; + use dep::aztec::constants_gen::PRIVATE_DATA_TREE_HEIGHT; + 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; struct Storage { notes: Map>, @@ -82,6 +90,40 @@ 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_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"); + } + + 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_membership_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_membership_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..ac0b96a9305 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'; @@ -99,11 +99,30 @@ 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.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, leafValue); + } + + 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'); + } } /** 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); } }