Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: historic access data tree #3159

Closed
wants to merge 13 commits into from
13 changes: 13 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,17 @@ jobs:
name: "Build"
command: build end-to-end

e2e-stateful-test-contract:
machine:
image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST= e2e_stateful_test_contract.test.ts

e2e-2-pxes:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -1354,6 +1365,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-sandbox-example: *e2e_test
Expand Down Expand Up @@ -1388,6 +1400,7 @@ workflows:
requires:
- e2e-2-pxes
- e2e-deploy-contract
- e2e-stateful-test-contract
- e2e-lending-contract
- e2e-token-contract
- e2e-sandbox-example
Expand Down
7 changes: 6 additions & 1 deletion yarn-project/acir-simulator/src/acvm/oracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +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 { UnencryptedL2Log } from '@aztec/types';
import { MerkleTreeId, UnencryptedL2Log } from '@aztec/types';

import { ACVMField } from '../acvm.js';
import { convertACVMFieldToBuffer, fromACVMField } from '../deserialize.js';
Expand Down Expand Up @@ -127,6 +127,11 @@ export class Oracle {
return toAcvmL1ToL2MessageLoadOracleInputs(message, root);
}

async getMembershipWitness([treeId]: ACVMField[], [leafValue]: ACVMField[]): Promise<ACVMField[]> {
const merkleTreeId: MerkleTreeId = Number(fromACVMField(treeId).toBigInt());
return (await this.typedOracle.getMembershipWitness(merkleTreeId, fromACVMField(leafValue))).map(toACVMField);
}

async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise<ACVMField> {
const contractAddress = AztecAddress.fromString(aztecAddress);
const portalContactAddress = await this.typedOracle.getPortalContractAddress(contractAddress);
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, Note, PublicKey, UnencryptedL2Log } from '@aztec/types';
import { CompleteAddress, MerkleTreeId, Note, PublicKey, UnencryptedL2Log } from '@aztec/types';

/**
* Information about a note needed during execution.
Expand Down Expand Up @@ -109,6 +109,10 @@ export abstract class TypedOracle {
throw new Error('Not available.');
}

getMembershipWitness(_treeId: MerkleTreeId, _leafValue: Fr): Promise<Fr[]> {
throw new Error('Not available.');
}

getPortalContractAddress(_contractAddress: AztecAddress): Promise<EthAddress> {
throw new Error('Not available.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,39 @@ export class ClientExecutionContext extends ViewDataOracle {
return notes;
}

/**
* Fetches a path to prove existence of a commitment in the db, given its contract side commitment (before silo).
* @param nonce - The nonce of the note.
* @param innerNoteHash - The inner note hash of the note.
* @returns 1 if (persistent or transient) note hash exists, 0 otherwise. Value is in ACVMField form.
*/
public async checkNoteHashExists(nonce: Fr, innerNoteHash: Fr): Promise<boolean> {
if (nonce.isZero()) {
// If nonce is 0, we are looking for a new note created in this transaction.
const exists = this.noteCache.checkNoteExists(this.contractAddress, innerNoteHash);
if (exists) {
return true;
}
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)
// Currently it can also be a note created from public if nonce is 0.
// If we can't find a matching new note, keep looking for the match from the notes created in previous transactions.
}

// If nonce is zero, SHOULD only be able to reach this point if note was publicly created
const wasm = await CircuitsWasm.get();
let noteHashToLookUp = siloCommitment(wasm, this.contractAddress, innerNoteHash);
if (!nonce.isZero()) {
noteHashToLookUp = computeUniqueCommitment(wasm, nonce, noteHashToLookUp);
}

const index = await this.db.getCommitmentIndex(noteHashToLookUp.toBuffer());
const exists = index !== undefined;
if (exists) {
this.gotNotes.set(noteHashToLookUp.value, index);
}
return exists;
}

/**
* Keep track of the new note created during execution.
* It can be used in subsequent calls (or transactions when chaining txs is possible).
Expand Down Expand Up @@ -305,7 +338,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 targetArtifact = await this.db.getFunctionArtifact(targetContractAddress, functionSelector);
Expand Down
16 changes: 16 additions & 0 deletions yarn-project/acir-simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FunctionArtifact, FunctionDebugMetadata, FunctionSelector } from '@azte
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';
Expand Down Expand Up @@ -107,4 +108,19 @@ export interface DBOracle extends CommitmentsDB {
* @returns A Promise that resolves to a HistoricBlockData object.
*/
getHistoricBlockData(): Promise<HistoricBlockData>;

/**
* 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<bigint | undefined>;

/**
* 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<Fr[]>;
}
19 changes: 4 additions & 15 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
CircuitsWasm,
CompleteAddress,
ContractDeploymentData,
EMPTY_NULLIFIED_COMMITMENT,
FieldsOf,
FunctionData,
HistoricBlockData,
Expand All @@ -19,7 +18,7 @@ import {
computeCommitmentNonce,
computeSecretMessageHash,
computeVarArgsHash,
siloCommitment,
siloCommitment
} from '@aztec/circuits.js/abis';
import { pedersenHashInputs } from '@aztec/circuits.js/barretenberg';
import { makeContractDeploymentData } from '@aztec/circuits.js/factories';
Expand All @@ -44,7 +43,7 @@ import { Note, PackedArguments, TxExecutionRequest } from '@aztec/types';
import { jest } from '@jest/globals';
import { MockProxy, mock } from 'jest-mock-extended';
import { default as levelup } from 'levelup';
import { type MemDown, default as memdown } from 'memdown';
import { default as memdown, type MemDown } from 'memdown';
import { getFunctionSelector } from 'viem';

import { buildL1ToL2Message, getFunctionArtifact, getFunctionArtifactWithSelector } from '../test/utils.js';
Expand Down Expand Up @@ -484,20 +483,10 @@ describe('Private Execution test suite', () => {
const secretHash = computeSecretMessageHash(wasm, secret);
const note = new Note([new Fr(amount), secretHash]);
const noteHash = hashFields(note.items);
const storageSlot = new Fr(5);
const storageSlot = new Fr(2);
const innerNoteHash = hashFields([storageSlot, noteHash]);
const siloedNoteHash = siloCommitment(wasm, contractAddress, innerNoteHash);
oracle.getNotes.mockResolvedValue([
{
contractAddress,
storageSlot,
nonce: Fr.ZERO,
note,
innerNoteHash: new Fr(EMPTY_NULLIFIED_COMMITMENT),
siloedNullifier: Fr.random(),
index: 1n,
},
]);
oracle.getCommitmentIndex.mockResolvedValue(0n);

const result = await runSimulator({
artifact,
Expand Down
17 changes: 16 additions & 1 deletion yarn-project/acir-simulator/src/client/view_data_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { siloNullifier } 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';
Expand Down Expand Up @@ -115,6 +115,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<Fr[]> {
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.
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/acir-simulator/src/public/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export interface CommitmentsDB {
getL1ToL2Message(msgKey: Fr): Promise<MessageLoadOracleInputs>;

/**
* Gets the index of a commitment in the note hash tree.
* Find the index of the given commitment in the note hash tree.
* @param commitment - The commitment.
* @returns - The index of the commitment. Undefined if it does not exist in the tree.
* @returns The index of the commitment. Undefined if it does not exist in the tree.
*/
getCommitmentIndex(commitment: Fr): Promise<bigint | undefined>;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CallContext, FunctionData, FunctionSelector, GlobalVariables, HistoricBlockData } from '@aztec/circuits.js';
import { CallContext, CircuitsWasm, FunctionData, FunctionSelector, GlobalVariables, HistoricBlockData } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
Expand All @@ -17,6 +17,7 @@ import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution, PublicExecutionResult } from './execution.js';
import { executePublicFunction } from './executor.js';
import { ContractStorageActionsCollector } from './state_actions.js';
import { siloCommitment } from '@aztec/circuits.js/abis';

/**
* The execution context for a public tx simulation.
Expand Down Expand Up @@ -107,6 +108,21 @@ export class PublicExecutionContext extends TypedOracle {
return { ...message, root: this.historicBlockData.l1ToL2MessagesTreeRoot };
}

/**
* Fetches a path to prove existence of a commitment in the db, given its contract side commitment (before silo).
* @param nonce - The nonce of the note.
* @param innerNoteHash - The inner note hash of the note.
* @returns 1 if (persistent or transient) note hash exists, 0 otherwise. Value is in ACVMField form.
*/
public async checkNoteHashExists(nonce: Fr, innerNoteHash: Fr): Promise<boolean> {
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)
// 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);
return index !== undefined;
}

/**
* Emit an unencrypted log.
* @param log - The unencrypted log to be emitted.
Expand Down
11 changes: 8 additions & 3 deletions yarn-project/aztec-nr/aztec/src/messaging.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ 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
pub fn process_l1_to_l2_message(l1_to_l2_root: Field, storage_contract_address: Field, msg_key: Field, content: Field, secret: Field) -> Field{

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);
Expand Down
Loading