Skip to content
Merged
13 changes: 13 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,17 @@ jobs:
name: "Test"
command: cond_spot_run_tests end-to-end e2e_cross_chain_messaging.test.ts

e2e-public-to-private-messaging:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_tests end-to-end e2e_public_to_private_messaging.test.ts

e2e-account-contract:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -610,6 +621,7 @@ workflows:
- e2e-nested-contract: *e2e_test
- e2e-public-token-contract: *e2e_test
- e2e-cross-chain-messaging: *e2e_test
- e2e-public-to-private-messaging: *e2e_test
- e2e-account-contract: *e2e_test
- integration-l1-publisher: *e2e_test
- integration-archiver-l1-to-l2: *e2e_test
Expand All @@ -623,6 +635,7 @@ workflows:
- e2e-nested-contract
- e2e-public-token-contract
- e2e-cross-chain-messaging
- e2e-public-to-private-messaging
- e2e-account-contract
- integration-l1-publisher
- integration-archiver-l1-to-l2
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/acir-simulator/src/acvm/acvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export interface ACIRCallback {
enqueuePublicFunctionCall(params: ACVMField[]): Promise<ACVMField[]>;
storageRead(params: ACVMField[]): Promise<[ACVMField]>;
storageWrite(params: ACVMField[]): Promise<[ACVMField]>;
createCommitment(params: ACVMField[]): Promise<[ACVMField]>;
createL2ToL1Message(params: ACVMField[]): Promise<[ACVMField]>;
viewNotesPage(params: ACVMField[]): Promise<ACVMField[]>;
getCommitment(params: ACVMField[]): Promise<ACVMField[]>;
getL1ToL2Message(params: ACVMField[]): Promise<ACVMField[]>;
/**
* Oracle call used to emit an encrypted log.
Expand Down
21 changes: 19 additions & 2 deletions yarn-project/acir-simulator/src/acvm/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
PrivateCircuitPublicInputs,
PublicCallRequest,
} from '@aztec/circuits.js';
import { MessageLoadOracleInputs } from '../client/db_oracle.js';
import { CommitmentDataOracleInputs, MessageLoadOracleInputs } from '../client/db_oracle.js';
import { Fr } from '@aztec/foundation/fields';

// Utilities to write TS classes to ACVM Field arrays
Expand Down Expand Up @@ -126,7 +126,7 @@ export async function toAcvmEnqueuePublicFunctionResult(item: PublicCallRequest)
* @param l1ToL2MessagesTreeRoot - The L1 to L2 messages tree root
* @returns The Message Oracle Fields.
*/
export function toAcvmMessageLoadOracleInputs(
export function toAcvmL1ToL2MessageLoadOracleInputs(
messageLoadOracleInputs: MessageLoadOracleInputs,
l1ToL2MessagesTreeRoot: Fr,
): ACVMField[] {
Expand All @@ -138,6 +138,23 @@ export function toAcvmMessageLoadOracleInputs(
];
}

/**
* Converts the result of loading commitments to ACVM fields.
* @param commitmentLoadOracleInputs - The result of loading messages to convert.
* @param l1ToL2MessagesTreeRoot - The L1 to L2 messages tree root
* @returns The Message Oracle Fields.
*/
export function toAcvmCommitmentLoadOracleInputs(
messageLoadOracleInputs: CommitmentDataOracleInputs,
l1ToL2MessagesTreeRoot: Fr,
): ACVMField[] {
return [
toACVMField(messageLoadOracleInputs.commitment),
toACVMField(messageLoadOracleInputs.index),
toACVMField(l1ToL2MessagesTreeRoot),
];
}

/**
* Inserts a list of ACVM fields to a witness.
* @param witnessStartIndex - The index where to start inserting the fields.
Expand Down
31 changes: 28 additions & 3 deletions yarn-project/acir-simulator/src/client/client_execution_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ import {
createDummyNote,
fromACVMField,
toACVMField,
toAcvmMessageLoadOracleInputs,
toAcvmCommitmentLoadOracleInputs,
toAcvmL1ToL2MessageLoadOracleInputs,
} from '../acvm/index.js';
import { NoteLoadOracleInputs, DBOracle } from './db_oracle.js';

/**
* A type that wraps data with it's read request index
*/
type ACVMWithReadRequestIndex = {
/** The index of the data in the tree. */
index: bigint;
/** The formatted data. */
acvmData: ACVMField[];
};

/**
* The execution context for a client tx simulation.
*/
Expand Down Expand Up @@ -91,10 +102,24 @@ export class ClientTxExecutionContext {
/**
* Fetches the a message from the db, given its key.
* @param msgKey - A buffer representing the message key.
* @returns The message data
* @returns The l1 to l2 message data
*/
public async getL1ToL2Message(msgKey: Fr): Promise<ACVMField[]> {
const messageInputs = await this.db.getL1ToL2Message(msgKey);
return toAcvmMessageLoadOracleInputs(messageInputs, this.historicRoots.l1ToL2MessagesTreeRoot);
return toAcvmL1ToL2MessageLoadOracleInputs(messageInputs, this.historicRoots.l1ToL2MessagesTreeRoot);
}

/**
* Fetches a path to prove existence of a commitment in the db, given its contract side commitment (before silo).
* @param contractAddress - The contract address.
* @param commitment - The commitment.
* @returns The commitment data.
*/
public async getCommitment(contractAddress: AztecAddress, commitment: Fr): Promise<ACVMWithReadRequestIndex> {
const commitmentInputs = await this.db.getCommitmentOracle(contractAddress, commitment);
return {
acvmData: toAcvmCommitmentLoadOracleInputs(commitmentInputs, this.historicRoots.privateDataTreeRoot),
index: commitmentInputs.index,
};
}
}
63 changes: 61 additions & 2 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { Grumpkin, pedersenCompressInputs } from '@aztec/circuits.js/barretenberg';
import {
ARGS_LENGTH,
CallContext,
Expand All @@ -12,7 +12,7 @@ import {
PublicCallRequest,
TxContext,
} from '@aztec/circuits.js';
import { computeSecretMessageHash } from '@aztec/circuits.js/abis';
import { computeSecretMessageHash, siloCommitment } from '@aztec/circuits.js/abis';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer';
import { padArrayEnd } from '@aztec/foundation/collection';
Expand All @@ -24,6 +24,7 @@ import {
ChildAbi,
NonNativeTokenContractAbi,
ParentAbi,
PublicToPrivateContractAbi,
TestContractAbi,
ZkTokenContractAbi,
} from '@aztec/noir-contracts/examples';
Expand Down Expand Up @@ -437,6 +438,64 @@ describe('Private Execution test suite', () => {
const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO));
expect(newNullifiers).toHaveLength(1);
}, 30_000);

it('Should be able to consume a dummy public to private message', async () => {
const db = levelup(createMemDown());
const pedersen = new Pedersen(bbWasm);

const contractAddress = AztecAddress.random();
const amount = 100n;
const abi = PublicToPrivateContractAbi.functions.find(f => f.name === 'mintFromPublicMessage')!;

const wasm = await CircuitsWasm.get();
const secret = new Fr(1n);
const secretHash = computeSecretMessageHash(wasm, secret);
const commitment = Fr.fromBuffer(pedersenCompressInputs(wasm, [toBufferBE(amount, 32), secretHash.toBuffer()]));
const siloedCommitment = siloCommitment(wasm, contractAddress, commitment);

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.

somewhere in the code, can you add a comment on why it is called "silo"?

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.

Added in the siloCommitment function natspec


const tree: AppendOnlyTree = await newTree(
StandardTree,
db,
pedersen,
'privateDataTree',
PRIVATE_DATA_TREE_HEIGHT,
);

await tree.appendLeaves([siloedCommitment.toBuffer()]);

const privateDataTreeRoot = Fr.fromBuffer(tree.getRoot(false));
const historicRoots = new PrivateHistoricTreeRoots(Fr.ZERO, Fr.ZERO, Fr.ZERO, privateDataTreeRoot, Fr.ZERO);

oracle.getCommitmentOracle.mockImplementation(async () => {
// Check the calculated commitment is correct
return Promise.resolve({
commitment: siloedCommitment,
index: 0n,
siblingPath: (await tree.getSiblingPath(0n, false)).toFieldArray(),
});
});

const txRequest = new TxExecutionRequest(
AztecAddress.random(),
contractAddress,
new FunctionData(Buffer.alloc(4), true, true),
encodeArguments(abi, [amount, secret, recipient]),
Fr.random(),
txContext,
Fr.ZERO,
);

const result = await acirSimulator.run(txRequest, abi, contractAddress, EthAddress.ZERO, historicRoots);

// Check a nullifier has been created.
const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO));
expect(newNullifiers).toHaveLength(1);

// Check the commitment read request was created successfully.
const readRequests = result.callStackItem.publicInputs.readRequests.filter(field => !field.equals(Fr.ZERO));
expect(readRequests).toHaveLength(1);
expect(readRequests[0]).toEqual(commitment);
}, 30_000);
});

describe('enqueued calls', () => {
Expand Down
12 changes: 10 additions & 2 deletions yarn-project/acir-simulator/src/client/private_execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,14 @@ export class PrivateFunctionExecution {

return toAcvmCallPrivateStackItem(childExecutionResult.callStackItem);
},
getL1ToL2Message: ([msgKey]: ACVMField[]) => this.context.getL1ToL2Message(fromACVMField(msgKey)),

getL1ToL2Message: ([msgKey]: ACVMField[]) => {
return this.context.getL1ToL2Message(fromACVMField(msgKey));
},
getCommitment: async ([commitment]: ACVMField[]) => {
const commitmentData = await this.context.getCommitment(this.contractAddress, fromACVMField(commitment));
readRequestCommitmentIndices.push(commitmentData.index);
return commitmentData.acvmData;
},
debugLog: (fields: ACVMField[]) => {
this.log(fieldsToFormattedStr(fields));
return Promise.resolve([ZERO_ACVM_FIELD]);
Expand All @@ -218,6 +224,8 @@ export class PrivateFunctionExecution {
viewNotesPage: notAvailable,
storageRead: notAvailable,
storageWrite: notAvailable,
createCommitment: notAvailable,
createL2ToL1Message: notAvailable,
callPublicFunction: notAvailable,
emitEncryptedLog: async ([
acvmContractAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ export class UnconstrainedFunctionExecution {
return Promise.resolve([ZERO_ACVM_FIELD]);
},
getL1ToL2Message: ([msgKey]: ACVMField[]) => this.context.getL1ToL2Message(fromACVMField(msgKey)),
getCommitment: ([commitment]: ACVMField[]) =>
this.context
.getCommitment(this.contractAddress, fromACVMField(commitment))
.then(commitmentData => commitmentData.acvmData),
enqueuePublicFunctionCall: notAvailable,
notifyCreatedNote: notAvailable,
notifyNullifiedNote: notAvailable,
callPrivateFunction: notAvailable,
callPublicFunction: notAvailable,
storageRead: notAvailable,
storageWrite: notAvailable,
createCommitment: notAvailable,
createL2ToL1Message: notAvailable,
emitEncryptedLog: notAvailable,
});

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/acir-simulator/src/public/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ export interface PublicContractsDB {
/** 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>;
getCommitmentOracle(address: AztecAddress, commitment: Fr): Promise<CommitmentDataOracleInputs>;
getTreeRoots(): PrivateHistoricTreeRoots;
}
4 changes: 4 additions & 0 deletions yarn-project/acir-simulator/src/public/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface PublicExecutionResult {
execution: PublicExecution;
/** The return values of the function. */
returnValues: Fr[];
/** The new commitments to be inserted into the commitments tree. */
newCommitments: Fr[];
/** The new l2 to l1 messages generated in this call. */
newL2ToL1Messages: Fr[];
/** The contract storage reads performed by the function. */
contractStorageReads: ContractStorageRead[];
/** The contract storage update requests performed by the function. */
Expand Down
40 changes: 38 additions & 2 deletions yarn-project/acir-simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ 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 {
ACVMField,
ZERO_ACVM_FIELD,
acvm,
frToAztecAddress,
frToSelector,
fromACVMField,
toACVMField,
toACVMWitness,
toAcvmCommitmentLoadOracleInputs,
toAcvmL1ToL2MessageLoadOracleInputs,
} from '../acvm/index.js';
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution, PublicExecutionResult } from './execution.js';
import { ContractStorageActionsCollector } from './state_actions.js';
Expand Down Expand Up @@ -42,6 +53,8 @@ export class PublicExecutor {

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

const notAvailable = () => Promise.reject(`Built-in not available for public execution simulation`);
Expand All @@ -57,7 +70,18 @@ export class PublicExecutor {
emitEncryptedLog: notAvailable,
viewNotesPage: notAvailable,
debugLog: notAvailable,
getL1ToL2Message: notAvailable, // l1 to l2 messages in public contexts TODO: https://github.com/AztecProtocol/aztec-packages/issues/616

getL1ToL2Message: async ([msgKey]: ACVMField[]) => {
const messageInputs = await this.commitmentsDb.getL1ToL2Message(fromACVMField(msgKey));
return toAcvmL1ToL2MessageLoadOracleInputs(messageInputs, this.treeRoots.l1ToL2MessagesTreeRoot);
}, // l1 to l2 messages in public contexts TODO: https://github.com/AztecProtocol/aztec-packages/issues/616
getCommitment: async ([commitment]: ACVMField[]) => {
const commitmentInputs = await this.commitmentsDb.getCommitmentOracle(
execution.contractAddress,
fromACVMField(commitment),
);
return toAcvmCommitmentLoadOracleInputs(commitmentInputs, this.treeRoots.privateDataTreeRoot);
},
storageRead: async ([slot]) => {
const storageSlot = fromACVMField(slot);
const value = await storageActions.read(storageSlot);
Expand All @@ -72,6 +96,16 @@ export class PublicExecutor {
this.log(`Oracle storage write: slot=${storageSlot.toShortString()} value=${value.toString()}`);
return [toACVMField(newValue)];
},
createCommitment: async ([commitment]) => {
this.log('Creating commitment: ' + commitment.toString());

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.

do we need to print these long numbers?

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.

Just keeping with the style of the other oracle calls where they log what they do

newCommitments.push(fromACVMField(commitment));
return await Promise.resolve([ZERO_ACVM_FIELD]);
},
createL2ToL1Message: async ([message]) => {
this.log('Creating L2 to L1 message: ' + message.toString());
newL2ToL1Messages.push(fromACVMField(message));
return await Promise.resolve([ZERO_ACVM_FIELD]);
},
callPublicFunction: async ([address, functionSelector, ...args]) => {
this.log(`Public function call: addr=${address} selector=${functionSelector} args=${args.join(',')}`);
const childExecutionResult = await this.callPublicFunction(
Expand All @@ -92,6 +126,8 @@ export class PublicExecutor {

return {
execution,
newCommitments,
newL2ToL1Messages,
contractStorageReads,
contractStorageUpdateRequests,
returnValues,
Expand Down
Loading