Skip to content

Commit

Permalink
feat(public->private): create commitments in public contexts (in noir) (
Browse files Browse the repository at this point in the history
#810)

* temp

* feat: create commitments and l1 messages in a public call

* feat: consume public message in private context

* chore: formatting:fix

* refactor: implement with read requests

* feat: e2e consume public commitment test

* chore(ci): add to circle ci

* chore: fmt

fix: fmt

* fix: tidy up noir syntax

* fix: lint

* fix: update noir review comments

* fix: update nits

* chore(fmt)
  • Loading branch information
Maddiaa0 authored Jun 14, 2023
1 parent 568b35d commit 27dc70f
Show file tree
Hide file tree
Showing 30 changed files with 761 additions and 21 deletions.
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 @@ -448,6 +449,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);

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());
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

0 comments on commit 27dc70f

Please sign in to comment.