Skip to content

Commit

Permalink
feat(messaging): public cross chain message e2e (#841)
Browse files Browse the repository at this point in the history
* feat: public consumption and creation of cross chain messages

* feat: new nullifiers in public context unit test

* feat: public token e2e, refactor test suite

* feat: e2e public cross chain test

* chore(ci): add public cross chain to ci

* fix: typo

* refactor: move sha to field to foundation

* fix: clean

* clean

* fix: add stop command to harness

* fix: nits

* fix: lint
  • Loading branch information
Maddiaa0 authored Jun 16, 2023
1 parent 4ad7eb4 commit 9f501ba
Show file tree
Hide file tree
Showing 27 changed files with 1,092 additions and 379 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-cross-chain-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_cross_chain_messaging.test.ts

e2e-public-to-private-messaging:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -621,6 +632,7 @@ workflows:
- e2e-nested-contract: *e2e_test
- e2e-public-token-contract: *e2e_test
- e2e-cross-chain-messaging: *e2e_test
- e2e-public-cross-chain-messaging: *e2e_test
- e2e-public-to-private-messaging: *e2e_test
- e2e-account-contract: *e2e_test
- integration-l1-publisher: *e2e_test
Expand All @@ -635,6 +647,7 @@ workflows:
- e2e-nested-contract
- e2e-public-token-contract
- e2e-cross-chain-messaging
- e2e-public-cross-chain-messaging
- e2e-public-to-private-messaging
- e2e-account-contract
- integration-l1-publisher
Expand Down
1 change: 1 addition & 0 deletions yarn-project/acir-simulator/src/acvm/acvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface ACIRCallback {
storageWrite(params: ACVMField[]): Promise<[ACVMField]>;
createCommitment(params: ACVMField[]): Promise<[ACVMField]>;
createL2ToL1Message(params: ACVMField[]): Promise<[ACVMField]>;
createNullifier(params: ACVMField[]): Promise<[ACVMField]>;
viewNotesPage(params: ACVMField[]): Promise<ACVMField[]>;
getCommitment(params: ACVMField[]): Promise<ACVMField[]>;
getL1ToL2Message(params: ACVMField[]): Promise<ACVMField[]>;
Expand Down
75 changes: 28 additions & 47 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import {
import { computeSecretMessageHash, siloCommitment } from '@aztec/circuits.js/abis';
import { Grumpkin, pedersenCompressInputs } from '@aztec/circuits.js/barretenberg';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer';
import { toBufferBE } from '@aztec/foundation/bigint-buffer';
import { padArrayEnd } from '@aztec/foundation/collection';
import { sha256 } from '@aztec/foundation/crypto';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr, Point } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
Expand All @@ -29,25 +28,26 @@ import {
TestContractAbi,
ZkTokenContractAbi,
} from '@aztec/noir-contracts/examples';
import { L1Actor, L1ToL2Message, L2Actor, TxExecutionRequest } from '@aztec/types';
import { TxExecutionRequest } from '@aztec/types';
import { mock } from 'jest-mock-extended';
import { default as levelup } from 'levelup';
import { default as memdown, type MemDown } from 'memdown';
import { encodeArguments } from '../abi_coder/index.js';
import { NoirPoint, computeSlotForMapping, toPublicKey } from '../utils.js';
import { DBOracle } from './db_oracle.js';
import { AcirSimulator } from './simulator.js';
import { buildL1ToL2Message } from '../test/utils.js';

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

describe('Private Execution test suite', () => {
let bbWasm: CircuitsWasm;
let circuitsWasm: CircuitsWasm;
let oracle: ReturnType<typeof mock<DBOracle>>;
let acirSimulator: AcirSimulator;
let logger: DebugLogger;

beforeAll(async () => {
bbWasm = await CircuitsWasm.get();
circuitsWasm = await CircuitsWasm.get();
logger = createDebugLogger('aztec:test:private_execution');
});

Expand Down Expand Up @@ -102,7 +102,7 @@ describe('Private Execution test suite', () => {
ownerPk = Buffer.from('5e30a2f886b4b6a11aea03bf4910fbd5b24e61aa27ea4d05c393b3ab592a8d33', 'hex');
recipientPk = Buffer.from('0c9ed344548e8f9ba8aa3c9f8651eaa2853130f6c1e9c050ccf198f7ea18a7ec', 'hex');

const grumpkin = new Grumpkin(bbWasm);
const grumpkin = new Grumpkin(circuitsWasm);
owner = toPublicKey(ownerPk, grumpkin);
recipient = toPublicKey(recipientPk, grumpkin);
});
Expand All @@ -125,13 +125,13 @@ describe('Private Execution test suite', () => {

expect(result.preimages.newNotes).toHaveLength(1);
const newNote = result.preimages.newNotes[0];
expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner, bbWasm));
expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner, circuitsWasm));

const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO));
expect(newCommitments).toHaveLength(1);

const [commitment] = newCommitments;
expect(commitment).toEqual(Fr.fromBuffer(acirSimulator.computeNoteHash(newNote.preimage, bbWasm)));
expect(commitment).toEqual(Fr.fromBuffer(acirSimulator.computeNoteHash(newNote.preimage, circuitsWasm)));
}, 30_000);

it('should run the mint function', async () => {
Expand All @@ -152,18 +152,18 @@ describe('Private Execution test suite', () => {

expect(result.preimages.newNotes).toHaveLength(1);
const newNote = result.preimages.newNotes[0];
expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner, bbWasm));
expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner, circuitsWasm));

const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO));
expect(newCommitments).toHaveLength(1);

const [commitment] = newCommitments;
expect(commitment).toEqual(Fr.fromBuffer(acirSimulator.computeNoteHash(newNote.preimage, bbWasm)));
expect(commitment).toEqual(Fr.fromBuffer(acirSimulator.computeNoteHash(newNote.preimage, circuitsWasm)));
});

it('should run the transfer function', async () => {
const db = levelup(createMemDown());
const pedersen = new Pedersen(bbWasm);
const pedersen = new Pedersen(circuitsWasm);

const contractAddress = AztecAddress.random();
const amountToTransfer = 100n;
Expand All @@ -172,7 +172,7 @@ describe('Private Execution test suite', () => {
const tree: AppendOnlyTree = await newTree(StandardTree, db, pedersen, 'privateData', PRIVATE_DATA_TREE_HEIGHT);
const preimages = [buildNote(60n, owner), buildNote(80n, owner)];
// TODO for this we need that noir siloes the commitment the same way as the kernel does, to do merkle membership
await tree.appendLeaves(preimages.map(preimage => acirSimulator.computeNoteHash(preimage, bbWasm)));
await tree.appendLeaves(preimages.map(preimage => acirSimulator.computeNoteHash(preimage, circuitsWasm)));

const historicRoots = new PrivateHistoricTreeRoots(
Fr.fromBuffer(tree.getRoot(false)),
Expand Down Expand Up @@ -213,30 +213,32 @@ describe('Private Execution test suite', () => {
expect(newNullifiers).toHaveLength(2);

expect(newNullifiers).toEqual(
preimages.map(preimage => Fr.fromBuffer(acirSimulator.computeNullifier(preimage, ownerPk, bbWasm))),
preimages.map(preimage => Fr.fromBuffer(acirSimulator.computeNullifier(preimage, ownerPk, circuitsWasm))),
);

expect(result.preimages.newNotes).toHaveLength(2);
const [recipientNote, changeNote] = result.preimages.newNotes;
expect(recipientNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), recipient, bbWasm));
expect(recipientNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), recipient, circuitsWasm));

const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO));

expect(newCommitments).toHaveLength(2);

const [recipientNoteCommitment, changeNoteCommitment] = newCommitments;
expect(recipientNoteCommitment).toEqual(
Fr.fromBuffer(acirSimulator.computeNoteHash(recipientNote.preimage, bbWasm)),
Fr.fromBuffer(acirSimulator.computeNoteHash(recipientNote.preimage, circuitsWasm)),
);
expect(changeNoteCommitment).toEqual(
Fr.fromBuffer(acirSimulator.computeNoteHash(changeNote.preimage, circuitsWasm)),
);
expect(changeNoteCommitment).toEqual(Fr.fromBuffer(acirSimulator.computeNoteHash(changeNote.preimage, bbWasm)));

expect(recipientNote.preimage[5]).toEqual(new Fr(amountToTransfer));
expect(changeNote.preimage[5]).toEqual(new Fr(40n));
}, 30_000);

it('should be able to transfer with dummy notes', async () => {
const db = levelup(createMemDown());
const pedersen = new Pedersen(bbWasm);
const pedersen = new Pedersen(circuitsWasm);

const contractAddress = AztecAddress.random();
const amountToTransfer = 100n;
Expand All @@ -246,7 +248,7 @@ describe('Private Execution test suite', () => {
const tree: AppendOnlyTree = await newTree(StandardTree, db, pedersen, 'privateData', PRIVATE_DATA_TREE_HEIGHT);
const preimages = [buildNote(balance, owner)];
// TODO for this we need that noir siloes the commitment the same way as the kernel does, to do merkle membership
await tree.appendLeaves(preimages.map(preimage => acirSimulator.computeNoteHash(preimage, bbWasm)));
await tree.appendLeaves(preimages.map(preimage => acirSimulator.computeNoteHash(preimage, circuitsWasm)));

const historicRoots = new PrivateHistoricTreeRoots(
Fr.fromBuffer(tree.getRoot(false)),
Expand Down Expand Up @@ -285,7 +287,9 @@ describe('Private Execution test suite', () => {
const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO));
expect(newNullifiers).toHaveLength(2);

expect(newNullifiers[0]).toEqual(Fr.fromBuffer(acirSimulator.computeNullifier(preimages[0], ownerPk, bbWasm)));
expect(newNullifiers[0]).toEqual(
Fr.fromBuffer(acirSimulator.computeNullifier(preimages[0], ownerPk, circuitsWasm)),
);

expect(result.preimages.newNotes).toHaveLength(2);
const [recipientNote, changeNote] = result.preimages.newNotes;
Expand Down Expand Up @@ -361,49 +365,26 @@ describe('Private Execution test suite', () => {
let recipientPk: Buffer;
let recipient: NoirPoint;

const buildL1ToL2Message = async (contentPreimage: Fr[], targetContract: AztecAddress, secret: Fr) => {
const wasm = await CircuitsWasm.get();

// Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)')
const contentBuf = Buffer.concat([
Buffer.from([0xee, 0xb7, 0x30, 0x71]),
...contentPreimage.map(field => field.toBuffer()),
]);
const temp = toBigIntBE(sha256(contentBuf));
const content = Fr.fromBuffer(toBufferBE(temp % Fr.MODULUS, 32));

const secretHash = computeSecretMessageHash(wasm, secret);

// Eventually the kernel will need to prove the kernel portal pair exists within the contract tree,
// EthAddress.random() will need to be replaced when this happens
return new L1ToL2Message(
new L1Actor(EthAddress.random(), 1),
new L2Actor(targetContract, 1),
content,
secretHash,
0,
0,
);
};

beforeAll(() => {
recipientPk = Buffer.from('0c9ed344548e8f9ba8aa3c9f8651eaa2853130f6c1e9c050ccf198f7ea18a7ec', 'hex');

const grumpkin = new Grumpkin(bbWasm);
const grumpkin = new Grumpkin(circuitsWasm);
recipient = toPublicKey(recipientPk, grumpkin);
});

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

const contractAddress = AztecAddress.random();
const bridgedAmount = 100n;
const abi = NonNativeTokenContractAbi.functions.find(f => f.name === 'mint')!;

const secret = new Fr(1n);
const canceller = EthAddress.random();
// Function selector: 0xeeb73071 keccak256('mint(uint256,bytes32,address)')
const preimage = await buildL1ToL2Message(
'eeb73071',
[new Fr(bridgedAmount), new Fr(recipient.x), canceller.toField()],
contractAddress,
secret,
Expand Down Expand Up @@ -452,7 +433,7 @@ describe('Private Execution test suite', () => {

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

const contractAddress = AztecAddress.random();
const amount = 100n;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export class PrivateFunctionExecution {
storageWrite: notAvailable,
createCommitment: notAvailable,
createL2ToL1Message: notAvailable,
createNullifier: notAvailable,
callPublicFunction: notAvailable,
emitUnencryptedLog: notAvailable,
emitEncryptedLog: async ([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class UnconstrainedFunctionExecution {
storageWrite: notAvailable,
createCommitment: notAvailable,
createL2ToL1Message: notAvailable,
createNullifier: notAvailable,
emitEncryptedLog: notAvailable,
emitUnencryptedLog: notAvailable,
});
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/acir-simulator/src/public/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface PublicExecutionResult {
newCommitments: Fr[];
/** The new l2 to l1 messages generated in this call. */
newL2ToL1Messages: Fr[];
/** The new nullifiers to be inserted into the nullifier tree. */
newNullifiers: Fr[];
/** The contract storage reads performed by the function. */
contractStorageReads: ContractStorageRead[];
/** The contract storage update requests performed by the function. */
Expand Down
13 changes: 12 additions & 1 deletion yarn-project/acir-simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
import { PublicExecution, PublicExecutionResult } from './execution.js';
import { ContractStorageActionsCollector } from './state_actions.js';
import { fieldsToFormattedStr } from '../client/debug.js';

// Copied from crate::abi at noir-contracts/src/contracts/noir-aztec3/src/abi.nr
const NOIR_MAX_RETURN_VALUES = 4;
Expand Down Expand Up @@ -56,6 +57,7 @@ export class PublicExecutor {
const storageActions = new ContractStorageActionsCollector(this.stateDb, execution.contractAddress);
const newCommitments: Fr[] = [];
const newL2ToL1Messages: Fr[] = [];
const newNullifiers: Fr[] = [];
const nestedExecutions: PublicExecutionResult[] = [];
const unencryptedLogs = new FunctionL2Logs([]);

Expand All @@ -71,8 +73,11 @@ export class PublicExecutor {
enqueuePublicFunctionCall: notAvailable,
emitEncryptedLog: notAvailable,
viewNotesPage: notAvailable,
debugLog: notAvailable,

debugLog: (fields: ACVMField[]) => {
this.log(fieldsToFormattedStr(fields));
return Promise.resolve([ZERO_ACVM_FIELD]);
},
getL1ToL2Message: async ([msgKey]: ACVMField[]) => {
const messageInputs = await this.commitmentsDb.getL1ToL2Message(fromACVMField(msgKey));
return toAcvmL1ToL2MessageLoadOracleInputs(messageInputs, this.treeRoots.l1ToL2MessagesTreeRoot);
Expand Down Expand Up @@ -108,6 +113,11 @@ export class PublicExecutor {
newL2ToL1Messages.push(fromACVMField(message));
return await Promise.resolve([ZERO_ACVM_FIELD]);
},
createNullifier: async ([nullifier]) => {
this.log('Creating nullifier: ' + nullifier.toString());
newNullifiers.push(fromACVMField(nullifier));
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 @@ -134,6 +144,7 @@ export class PublicExecutor {
execution,
newCommitments,
newL2ToL1Messages,
newNullifiers,
contractStorageReads,
contractStorageUpdateRequests,
returnValues,
Expand Down
Loading

0 comments on commit 9f501ba

Please sign in to comment.