Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ WASM_EXPORT void abis__compute_contract_leaf(uint8_t const* contract_leaf_preima
NT::fr::serialize_to_buffer(to_write, output);
}

/**
* @brief Generates a siloed commitment tree leaf from the contract and the commitment.
*/
CBIND(abis__silo_commitment, aztec3::circuits::silo_commitment<NT>);

/**
* @brief Generates a signed tx request hash from it's pre-image
* This is a WASM-export that can be called from Typescript.
Expand Down
3 changes: 3 additions & 0 deletions circuits/cpp/src/aztec3/circuits/abis/c_bind.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "aztec3/circuits/hash.hpp"

#include <barretenberg/barretenberg.hpp>

#include <algorithm>
Expand All @@ -22,6 +24,7 @@ WASM_EXPORT void abis__hash_constructor(uint8_t const* func_data_buf,
uint8_t* output);

CBIND_DECL(abis__compute_contract_address);
CBIND_DECL(abis__silo_commitment);

WASM_EXPORT void abis__compute_message_secret_hash(uint8_t const* secret, uint8_t* output);
WASM_EXPORT void abis__compute_contract_leaf(uint8_t const* contract_leaf_preimage_buf, uint8_t* output);
Expand Down
20 changes: 18 additions & 2 deletions yarn-project/acir-simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { FunctionAbi } from '@aztec/foundation/abi';
import { CommitmentsDB } from '../index.js';

/**
* The format that noir contracts use to get notes.
Expand Down Expand Up @@ -40,10 +41,26 @@ export interface MessageLoadOracleInputs {
index: bigint;
}

/**
* The format noir uses to get commitments.
*/
export interface CommitmentDataOracleInputs {
/** The siloed commitment. */
commitment: Fr;
/**
* The path in the merkle tree to the commitment.
*/
siblingPath: Fr[];
/**
* The index of the message commitment in the merkle tree.
*/
index: bigint;
}

/**
* The database oracle interface.
*/
export interface DBOracle {
export interface DBOracle extends CommitmentsDB {
getSecretKey(contractAddress: AztecAddress, address: AztecAddress): Promise<Buffer>;
getNotes(
contractAddress: AztecAddress,
Expand All @@ -58,5 +75,4 @@ export interface DBOracle {
}>;
getFunctionABI(contractAddress: AztecAddress, functionSelector: Buffer): Promise<FunctionAbi>;
getPortalContractAddress(contractAddress: AztecAddress): Promise<EthAddress>;
getL1ToL2Message(msgKey: Fr): Promise<MessageLoadOracleInputs>;
}
10 changes: 9 additions & 1 deletion yarn-project/acir-simulator/src/public/db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EthAddress } from '@aztec/circuits.js';
import { EthAddress, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { CommitmentDataOracleInputs, MessageLoadOracleInputs } from '../index.js';

/**
* Database interface for providing access to public state.
Expand Down Expand Up @@ -42,3 +43,10 @@ export interface PublicContractsDB {
*/
getPortalContractAddress(address: AztecAddress): Promise<EthAddress | undefined>;
}

/** Database interface for providing access to commitment tree and l1 to l2 messages tree (append only data trees). */
export interface CommitmentsDB {
getL1ToL2Message(msgKey: Fr): Promise<MessageLoadOracleInputs>;
getCommitmentOracle(address: AztecAddress, msgKey: Fr): Promise<CommitmentDataOracleInputs>;
getTreeRoots(): PrivateHistoricTreeRoots;
}
29 changes: 22 additions & 7 deletions yarn-project/acir-simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AztecAddress, CallContext, EthAddress, Fr, FunctionData } from '@aztec/circuits.js';
import { AztecAddress, CallContext, EthAddress, Fr, FunctionData, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
import { padArrayEnd } from '@aztec/foundation/collection';
import { createDebugLogger } from '@aztec/foundation/log';
import { TxExecutionRequest } from '@aztec/types';
import { select_return_flattened as selectPublicWitnessFlattened } from '@noir-lang/noir_util_wasm';
import { acvm, frToAztecAddress, frToSelector, fromACVMField, toACVMField, toACVMWitness } from '../acvm/index.js';
import { PublicContractsDB, PublicStateDB } from './db.js';
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution, PublicExecutionResult } from './execution.js';
import { ContractStorageActionsCollector } from './state_actions.js';

Expand All @@ -15,12 +15,17 @@ const NOIR_MAX_RETURN_VALUES = 4;
* Handles execution of public functions.
*/
export class PublicExecutor {
private treeRoots: PrivateHistoricTreeRoots;
constructor(
private readonly stateDb: PublicStateDB,
private readonly contractsDb: PublicContractsDB,
private readonly commitmentsDb: CommitmentsDB,

private log = createDebugLogger('aztec:simulator:public-executor'),
) {}
) {
// Store the tree roots on instantiation.
this.treeRoots = this.commitmentsDb.getTreeRoots();
}

/**
* Executes a public execution request.
Expand All @@ -35,7 +40,7 @@ export class PublicExecutor {
const acir = await this.contractsDb.getBytecode(execution.contractAddress, selector);
if (!acir) throw new Error(`Bytecode not found for ${execution.contractAddress.toShortString()}:${selectorHex}`);

const initialWitness = getInitialWitness(execution.args, execution.callContext);
const initialWitness = getInitialWitness(execution.args, execution.callContext, this.treeRoots);
const storageActions = new ContractStorageActionsCollector(this.stateDb, execution.contractAddress);
const nestedExecutions: PublicExecutionResult[] = [];

Expand All @@ -52,8 +57,7 @@ export class PublicExecutor {
emitEncryptedLog: notAvailable,
viewNotesPage: notAvailable,
debugLog: notAvailable,
// l1 to l2 messages in public contexts TODO: https://github.com/AztecProtocol/aztec-packages/issues/616
getL1ToL2Message: notAvailable,
getL1ToL2Message: notAvailable, // l1 to l2 messages in public contexts TODO: https://github.com/AztecProtocol/aztec-packages/issues/616
storageRead: async ([slot]) => {
const storageSlot = fromACVMField(slot);
const value = await storageActions.read(storageSlot);
Expand Down Expand Up @@ -144,14 +148,25 @@ export class PublicExecutor {
* @param witnessStartIndex - The index where to start inserting the parameters.
* @returns The initial witness.
*/
function getInitialWitness(args: Fr[], callContext: CallContext, witnessStartIndex = 1) {
function getInitialWitness(
args: Fr[],
callContext: CallContext,
commitmentTreeRoots: PrivateHistoricTreeRoots,
witnessStartIndex = 1,
) {
return toACVMWitness(witnessStartIndex, [
callContext.isContractDeployment,
callContext.isDelegateCall,
callContext.isStaticCall,
callContext.msgSender,
callContext.portalContractAddress,
callContext.storageContractAddress,

commitmentTreeRoots.contractTreeRoot,
commitmentTreeRoots.l1ToL2MessagesTreeRoot,
commitmentTreeRoots.nullifierTreeRoot,
commitmentTreeRoots.privateDataTreeRoot,

...args,
]);
}
11 changes: 7 additions & 4 deletions yarn-project/acir-simulator/src/public/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CallContext, CircuitsWasm, FunctionData } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { CallContext, FunctionData, CircuitsWasm, PrivateHistoricTreeRoots } 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 @@ -8,19 +9,19 @@ import { MockProxy, mock } from 'jest-mock-extended';
import { default as memdown, type MemDown } from 'memdown';
import { encodeArguments } from '../abi_coder/encoder.js';
import { NoirPoint, computeSlotForMapping, toPublicKey } from '../utils.js';
import { PublicContractsDB, PublicStateDB } from './db.js';
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution } from './execution.js';
import { PublicExecutor } from './executor.js';
import { toBigInt } from '@aztec/foundation/serialize';
import { keccak } from '@aztec/foundation/crypto';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';

export const createMemDown = () => (memdown as any)() as MemDown<any, any>;

describe('ACIR public execution simulator', () => {
let bbWasm: CircuitsWasm;
let publicState: MockProxy<PublicStateDB>;
let publicContracts: MockProxy<PublicContractsDB>;
let commitmentsDb: MockProxy<CommitmentsDB>;
let executor: PublicExecutor;

beforeAll(async () => {
Expand All @@ -30,8 +31,10 @@ describe('ACIR public execution simulator', () => {
beforeEach(() => {
publicState = mock<PublicStateDB>();
publicContracts = mock<PublicContractsDB>();
commitmentsDb = mock<CommitmentsDB>();

executor = new PublicExecutor(publicState, publicContracts);
commitmentsDb.getTreeRoots.mockReturnValue(PrivateHistoricTreeRoots.empty());
executor = new PublicExecutor(publicState, publicContracts, commitmentsDb);
});

describe('PublicToken contract', () => {
Expand Down
7 changes: 7 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/aztec-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ export interface AztecNode {
*/
getContractPath(leafIndex: bigint): Promise<SiblingPath<typeof CONTRACT_TREE_HEIGHT>>;

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should we handle duplicate leafs? The first one you know have not been spent? Just thinking with the idea proposed around also putting L1 to L2 in the commitments tree, would be a pity if you keep fetching the wrong index and cannot spend the note because there was two insertions with "deposit 10 eth to A".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My current idea is that if you have a transparent commitment then the nullifier's will need to be created with a different process. (Including the index) In the end to end test I was hoping to make a library that does this, but you are correct it does feel like a footgun.


/**
* Returns the sibling path for the given index in the data tree.
* @param leafIndex - The index of the leaf for which the sibling path is required.
Expand Down
13 changes: 13 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/http-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,19 @@ export class HttpNode implements AztecNode {
return Promise.resolve(SiblingPath.fromString(path));
}

/**
* Find the index of the given piece of data.
* @param leafValue - The value to search for.
* @returns The index of the given leaf in the data tree or undefined if not found.
*/
async findCommitmentIndex(leafValue: Buffer): Promise<bigint | undefined> {
const url = new URL(`${this.baseUrl}/commitment-index`);
url.searchParams.append('leaf', leafValue.toString('hex'));
const response = await (await fetch(url.toString())).json();
const index = response.index as string;
return Promise.resolve(BigInt(index));
}

/**
* Returns the sibling path for the given index in the data tree.
* @param leafIndex - The index of the leaf for which the sibling path is required.
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,15 @@ export class AztecNodeService implements AztecNode {
return this.merkleTreeDB.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex, false);
}

/**
* Find the index of the given commitment.
* @param leafValue - The value to search for.
* @returns The index of the given leaf in the private data tree or undefined if not found.
*/
public findCommitmentIndex(leafValue: Buffer): Promise<bigint | undefined> {
return this.merkleTreeDB.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, leafValue, false);
}

/**
* Returns the sibling path for the given index in the data tree.
* @param leafIndex - The index of the leaf for which the sibling path is required.
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-rpc/src/account_state/account_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class AccountState {
);
const portalContract = await contractDataOracle.getPortalContractAddress(contractAddress);

const currentRoots = await this.db.getTreeRoots();
const currentRoots = this.db.getTreeRoots();
const historicRoots = PrivateHistoricTreeRoots.from({
contractTreeRoot: currentRoots[MerkleTreeId.CONTRACT_TREE],
nullifierTreeRoot: currentRoots[MerkleTreeId.NULLIFIER_TREE],
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-rpc/src/contract_tree/contract_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export class ContractTree {
const vkHash = hashVKStr(constructorAbi.verificationKey, wasm);
const argsHash = await computeVarArgsHash(wasm, args);
const constructorHash = hashConstructor(wasm, functionData, argsHash, vkHash);
const address = await computeContractAddress(wasm, from, contractAddressSalt, root, constructorHash);
const address = computeContractAddress(wasm, from, contractAddressSalt, root, constructorHash);
const contractDao: ContractDao = {
...abi,
address,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-rpc/src/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export interface Database extends ContractDatabase {
addTxAuxDataBatch(txAuxDataDaos: TxAuxDataDao[]): Promise<void>;
removeNullifiedTxAuxData(nullifiers: Fr[], account: Point): Promise<TxAuxDataDao[]>;

getTreeRoots(): Promise<Record<MerkleTreeId, Fr>>;
getTreeRoots(): Record<MerkleTreeId, Fr>;
Comment thread
LHerskind marked this conversation as resolved.
setTreeRoots(roots: Record<MerkleTreeId, Fr>): Promise<void>;
}
6 changes: 3 additions & 3 deletions yarn-project/aztec-rpc/src/database/memory_db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ export class MemoryDB extends MemoryContractDatabase implements Database {
* and their corresponding Fr values as roots. Throws an error if the tree roots are not set in the
* memory database.
*
* @returns A Promise that resolves to an object containing the Merkle tree roots for each merkle tree id.
* @returns An object containing the Merkle tree roots for each merkle tree id.
*/
public getTreeRoots(): Promise<Record<MerkleTreeId, Fr>> {
public getTreeRoots(): Record<MerkleTreeId, Fr> {
const roots = this.treeRoots;
if (!roots) throw new Error(`Tree roots not set in memory database`);
return Promise.resolve(roots);
return roots;
}

/**
Expand Down
16 changes: 5 additions & 11 deletions yarn-project/aztec-rpc/src/kernel_prover/proof_creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
privateKernelSimInner,
privateKernelSimInit,
} from '@aztec/circuits.js';
import { pedersenCompressWithHashIndex } from '@aztec/circuits.js/barretenberg';
import { siloCommitment } from '@aztec/circuits.js/abis';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';

Expand Down Expand Up @@ -39,8 +39,6 @@ export interface ProofCreator {
createProofInner(previousKernelData: PreviousKernelData, privateCallData: PrivateCallData): Promise<ProofOutput>;
}

const OUTER_COMMITMENT = 3;

/**
* The KernelProofCreator class is responsible for generating siloed commitments and zero-knowledge proofs
* for private kernel circuit. It leverages Barretenberg and Circuits Wasm libraries
Expand All @@ -58,14 +56,10 @@ export class KernelProofCreator {
* @returns An array of Fr (finite field) elements representing the siloed commitments.
*/
public async getSiloedCommitments(publicInputs: PrivateCircuitPublicInputs) {
const bbWasm = await CircuitsWasm.get();
const contractAddress = publicInputs.callContext.storageContractAddress.toBuffer();
// TODO
// Should match `add_contract_address_to_commitment` in hash.hpp.
// Should use a function exported from circuits.js.
return publicInputs.newCommitments.map(commitment =>
Fr.fromBuffer(pedersenCompressWithHashIndex(bbWasm, [contractAddress, commitment.toBuffer()], OUTER_COMMITMENT)),
);
const wasm = await CircuitsWasm.get();
const contractAddress = publicInputs.callContext.storageContractAddress;

return publicInputs.newCommitments.map(commitment => siloCommitment(wasm, contractAddress, commitment));
}

/**
Expand Down
40 changes: 37 additions & 3 deletions yarn-project/aztec-rpc/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { DBOracle, MessageLoadOracleInputs } from '@aztec/acir-simulator';
import { CommitmentDataOracleInputs, DBOracle, MessageLoadOracleInputs } from '@aztec/acir-simulator';
import { AztecNode } from '@aztec/aztec-node';
import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js';
import { AztecAddress, CircuitsWasm, EthAddress, Fr, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
import { KeyPair } from '@aztec/key-store';
import { FunctionAbi } from '@aztec/foundation/abi';
import { ContractDataOracle } from '../contract_data_oracle/index.js';
import { Database } from '../database/index.js';
import { siloCommitment } from '@aztec/circuits.js/abis';
import { MerkleTreeId } from '@aztec/types';

/**
* A data oracle that provides information needed for simulating a transaction.
Expand Down Expand Up @@ -93,7 +95,7 @@ export class SimulatorOracle implements DBOracle {
*
* @param msgKey - The key of the message to be retreived
* @returns A promise that resolves to the message data, a sibling path and the
* index of the message in the the l1ToL2MessagesTree
* index of the message in the l1ToL2MessagesTree
*/
async getL1ToL2Message(msgKey: Fr): Promise<MessageLoadOracleInputs> {
const messageAndIndex = await this.node.getL1ToL2MessageAndIndex(msgKey);
Expand All @@ -106,4 +108,36 @@ export class SimulatorOracle implements DBOracle {
index,
};
}

/**
* Retrieves the noir oracle data required to prove existence of a given commitment.
* @param contractAddress - The contract Address.
* @param commitment - The key of the message being fetched.
* @returns - A promise that resolves to the commitment data, a sibling path and the
* index of the message in the private data tree.
*/
async getCommitmentOracle(contractAddress: AztecAddress, commitment: Fr): Promise<CommitmentDataOracleInputs> {
const siloedCommitment = siloCommitment(await CircuitsWasm.get(), contractAddress, commitment);
const index = await this.node.findCommitmentIndex(siloedCommitment.toBuffer());
if (!index) throw new Error('Commitment not found');

const siblingPath = await this.node.getDataTreePath(index);
return await Promise.resolve({
commitment: siloedCommitment,
siblingPath: siblingPath.toFieldArray(),
index,
});
}

getTreeRoots(): PrivateHistoricTreeRoots {
const roots = this.db.getTreeRoots();

return PrivateHistoricTreeRoots.from({
privateKernelVkTreeRoot: Fr.ZERO,
privateDataTreeRoot: roots[MerkleTreeId.PRIVATE_DATA_TREE],
contractTreeRoot: roots[MerkleTreeId.CONTRACT_TREE],
nullifierTreeRoot: roots[MerkleTreeId.NULLIFIER_TREE],
l1ToL2MessagesTreeRoot: roots[MerkleTreeId.L1_TO_L2_MESSAGES_TREE],
});
}
}
Loading