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: noir brillig compiler #316

Merged
merged 20 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from 19 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
4 changes: 2 additions & 2 deletions yarn-project/acir-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"@aztec/foundation": "workspace:^",
"@aztec/merkle-tree": "workspace:^",
"@aztec/noir-contracts": "workspace:^",
"@noir-lang/aztec_backend_wasm": "github:joss-aztec/barretenberg_browser_stopgap_wasm_build#f2007d1c309da9d41c3ad38cc8696613678e910c",
"@noir-lang/noir_util_wasm": "github:joss-aztec/noir_util_wasm#bb71df87c9415be387c998b67cb0de4298ab6b6a",
"@noir-lang/aztec_backend_wasm": "github:joss-aztec/barretenberg_browser_stopgap_wasm_build#41d52a9ad86786c65ab9b0502927e9e2fa93d963",
"@noir-lang/noir_util_wasm": "github:joss-aztec/noir_util_wasm#94d5812260636794ab77c437261e6b814b130265",
"levelup": "^5.1.1",
"memdown": "^6.1.1",
"tslib": "^2.4.0"
Expand Down
13 changes: 7 additions & 6 deletions yarn-project/acir-simulator/src/acvm/acvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ export const ZERO_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES)}`;
export const ONE_ACVM_FIELD: ACVMField = `0x${'00'.repeat(Fr.SIZE_IN_BYTES - 1)}01`;

export interface ACIRCallback {
getSecretKey(params: ACVMField[]): Promise<ACVMField[]>;
getSecretKey(params: ACVMField[]): Promise<[ACVMField]>;
getNotes2(params: ACVMField[]): Promise<ACVMField[]>;
getRandomField(): Promise<ACVMField[]>;
notifyCreatedNote(params: ACVMField[]): Promise<ACVMField[]>;
notifyNullifiedNote(params: ACVMField[]): Promise<ACVMField[]>;
getRandomField(): Promise<[ACVMField]>;
notifyCreatedNote(params: ACVMField[]): Promise<[ACVMField]>;
notifyNullifiedNote(params: ACVMField[]): Promise<[ACVMField]>;
callPrivateFunction(params: ACVMField[]): Promise<ACVMField[]>;
storageRead(params: ACVMField[]): Promise<ACVMField[]>;
storageWrite(params: ACVMField[]): Promise<ACVMField[]>;
storageRead(params: ACVMField[]): Promise<[ACVMField]>;
storageWrite(params: ACVMField[]): Promise<[ACVMField]>;
viewNotesPage(params: ACVMField[]): Promise<ACVMField[]>;
}

export interface ACIRExecutionResult {
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/acir-simulator/src/acvm/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export function frToAztecAddress(fr: Fr): AztecAddress {
return new AztecAddress(fr.toBuffer());
}

export function frToNumber(fr: Fr): number {
return Number(fr.value);
}

export function frToEthAddress(fr: Fr): EthAddress {
return new EthAddress(fr.toBuffer().slice(-EthAddress.SIZE_IN_BYTES));
}
Expand Down
7 changes: 6 additions & 1 deletion yarn-project/acir-simulator/src/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export interface NoteLoadOracleInputs {

export interface DBOracle {
getSecretKey(contractAddress: AztecAddress, address: AztecAddress): Promise<Buffer>;
getNotes(contractAddress: AztecAddress, storageSlot: Fr, count: number): Promise<NoteLoadOracleInputs[]>;
getNotes(
contractAddress: AztecAddress,
storageSlot: Fr,
limit: number,
offset: number,
): Promise<{ count: number; notes: NoteLoadOracleInputs[] }>;
getFunctionABI(contractAddress: AztecAddress, functionSelector: Buffer): Promise<FunctionAbi>;
getPortalContractAddress(contractAddress: AztecAddress): Promise<EthAddress>;
}
69 changes: 40 additions & 29 deletions yarn-project/acir-simulator/src/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
PRIVATE_DATA_TREE_HEIGHT,
} from '@aztec/circuits.js';
import { DBOracle } from './db_oracle.js';
import { extractPublicInputs, frToAztecAddress, frToSelector } from './acvm/deserialize.js';
import { extractPublicInputs, frToAztecAddress, frToNumber, frToSelector } from './acvm/deserialize.js';
import { FunctionAbi } from '@aztec/noir-contracts';
import { createDebugLogger } from '@aztec/foundation/log';

Expand Down Expand Up @@ -53,6 +53,10 @@ export interface ExecutionResult {
nestedExecutions: this[];
}

const notAvailable = () => {
return Promise.reject(new Error(`Storage access not available for private function execution`));
};

export class Execution {
constructor(
// Global to the tx
Expand Down Expand Up @@ -83,12 +87,10 @@ export class Execution {
const nestedExecutionContexts: ExecutionResult[] = [];

const { partialWitness } = await acvm(acir, initialWitness, {
getSecretKey: ([address]: ACVMField[]) => {
return this.getSecretKey(this.contractAddress, address);
},
getNotes2: async ([, storageSlot]: ACVMField[]) => {
return await this.getNotes(this.contractAddress, storageSlot, 2);
},
getSecretKey: async ([address]: ACVMField[]) => [
toACVMField(await this.db.getSecretKey(this.contractAddress, frToAztecAddress(fromACVMField(address)))),
],
getNotes2: ([storageSlot]: ACVMField[]) => this.getNotes(this.contractAddress, storageSlot, 2),
getRandomField: () => Promise.resolve([toACVMField(Fr.random())]),
notifyCreatedNote: ([storageSlot, ownerX, ownerY, ...acvmPreimage]: ACVMField[]) => {
newNotePreimages.push({
Expand Down Expand Up @@ -121,12 +123,15 @@ export class Execution {

return toAcvmCallPrivateStackItem(childExecutionResult.callStackItem);
},
storageRead: () => {
return Promise.reject(new Error(`Storage access not available for private function execution`));
},
storageWrite: () => {
return Promise.reject(new Error(`Storage access not available for private function execution`));
},
viewNotesPage: ([acvmSlot, acvmLimit, acvmOffset]) =>
this.viewNotes(
this.contractAddress,
acvmSlot,
frToNumber(fromACVMField(acvmLimit)),
frToNumber(fromACVMField(acvmOffset)),
),
storageRead: notAvailable,
storageWrite: notAvailable,
});

const publicInputs = extractPublicInputs(partialWitness, acir);
Expand All @@ -146,27 +151,33 @@ export class Execution {
};
}

private async getNotes(contractAddress: AztecAddress, storageSlot: ACVMField, count: number) {
const notes = await this.db.getNotes(contractAddress, fromACVMField(storageSlot), count);
const dummyCount = Math.max(0, count - notes.length);
const dummyNotes = Array.from({ length: dummyCount }, () => ({
private async getNotes(contractAddress: AztecAddress, storageSlot: ACVMField, limit: number) {
const { count, notes } = await this.fetchNotes(contractAddress, storageSlot, limit);
return [
toACVMField(count),
...notes.flatMap(noteGetData => toAcvmNoteLoadOracleInputs(noteGetData, this.historicRoots.privateDataTreeRoot)),
];
}

private async viewNotes(contractAddress: AztecAddress, storageSlot: ACVMField, limit: number, offset = 0) {
const { count, notes } = await this.fetchNotes(contractAddress, storageSlot, limit, offset);

return [toACVMField(count), ...notes.flatMap(noteGetData => noteGetData.preimage.map(f => toACVMField(f)))];
}

private async fetchNotes(contractAddress: AztecAddress, storageSlot: ACVMField, limit: number, offset = 0) {
const { count, notes } = await this.db.getNotes(contractAddress, fromACVMField(storageSlot), limit, offset);

const dummyNotes = Array.from({ length: Math.max(0, limit - notes.length) }, () => ({
preimage: createDummyNote(),
siblingPath: new Array(PRIVATE_DATA_TREE_HEIGHT).fill(Fr.ZERO),
index: 0n,
}));

return notes
.concat(dummyNotes)
.flatMap(noteGetData => toAcvmNoteLoadOracleInputs(noteGetData, this.historicRoots.privateDataTreeRoot));
}

private async getSecretKey(contractAddress: AztecAddress, address: ACVMField) {
// TODO remove this when we have brillig oracles that don't execute on false branches
if (address === ZERO_ACVM_FIELD) {
return [ZERO_ACVM_FIELD];
}
const key = await this.db.getSecretKey(contractAddress, frToAztecAddress(fromACVMField(address)));
return [toACVMField(key)];
return {
count,
notes: notes.concat(dummyNotes),
};
}

private async callPrivateFunction(
Expand Down
56 changes: 31 additions & 25 deletions yarn-project/acir-simulator/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('ACIR simulator', () => {
);
const result = await acirSimulator.run(
txRequest,
TestContractAbi.functions[0] as FunctionAbi,
TestContractAbi.functions[0],
AztecAddress.ZERO,
EthAddress.ZERO,
historicRoots,
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('ACIR simulator', () => {
it('should a constructor with arguments that creates notes', async () => {
const historicRoots = new PrivateHistoricTreeRoots(new Fr(0n), new Fr(0n), new Fr(0n), new Fr(0n));
const contractAddress = AztecAddress.random();
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'constructor') as unknown as FunctionAbi;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'constructor')!;

const txRequest = new TxRequest(
AztecAddress.random(),
Expand All @@ -116,12 +116,12 @@ describe('ACIR simulator', () => {

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

it('should run the mint function', async () => {
const historicRoots = new PrivateHistoricTreeRoots(new Fr(0n), new Fr(0n), new Fr(0n), new Fr(0n));
const contractAddress = AztecAddress.random();
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'mint') as unknown as FunctionAbi;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'mint')!;

const txRequest = new TxRequest(
AztecAddress.random(),
Expand Down Expand Up @@ -151,7 +151,7 @@ describe('ACIR simulator', () => {

const contractAddress = AztecAddress.random();
const amountToTransfer = 100n;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'transfer') as unknown as FunctionAbi;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'transfer')!;

const tree: AppendOnlyTree = await newTree(StandardTree, db, pedersen, 'privateData', PRIVATE_DATA_TREE_HEIGHT);
const preimages = [buildNote(60n, owner), buildNote(80n, owner)];
Expand All @@ -165,14 +165,17 @@ describe('ACIR simulator', () => {
new Fr(0n),
);

oracle.getNotes.mockImplementation(() => {
return Promise.all(
preimages.map(async (preimage, index) => ({
preimage,
siblingPath: (await tree.getSiblingPath(BigInt(index), false)).data.map(buf => Fr.fromBuffer(buf)),
index: BigInt(index),
})),
);
oracle.getNotes.mockImplementation(async () => {
return {
count: preimages.length,
notes: await Promise.all(
preimages.map(async (preimage, index) => ({
preimage,
siblingPath: (await tree.getSiblingPath(BigInt(index), false)).data.map(buf => Fr.fromBuffer(buf)),
index: BigInt(index),
})),
),
};
});

oracle.getSecretKey.mockReturnValue(Promise.resolve(ownerPk));
Expand Down Expand Up @@ -222,7 +225,7 @@ describe('ACIR simulator', () => {
const contractAddress = AztecAddress.random();
const amountToTransfer = 100n;
const balance = 160n;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'transfer') as unknown as FunctionAbi;
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'transfer')!;

const tree: AppendOnlyTree = await newTree(StandardTree, db, pedersen, 'privateData', PRIVATE_DATA_TREE_HEIGHT);
const preimages = [buildNote(balance, owner)];
Expand All @@ -236,14 +239,17 @@ describe('ACIR simulator', () => {
new Fr(0n),
);

oracle.getNotes.mockImplementation(() => {
return Promise.all(
preimages.map(async (preimage, index) => ({
preimage,
siblingPath: (await tree.getSiblingPath(BigInt(index), false)).data.map(buf => Fr.fromBuffer(buf)),
index: BigInt(index),
})),
);
oracle.getNotes.mockImplementation(async () => {
return {
count: preimages.length,
notes: await Promise.all(
preimages.map(async (preimage, index) => ({
preimage,
siblingPath: (await tree.getSiblingPath(BigInt(index), false)).data.map(buf => Fr.fromBuffer(buf)),
index: BigInt(index),
})),
),
};
});

oracle.getSecretKey.mockReturnValue(Promise.resolve(ownerPk));
Expand Down Expand Up @@ -278,7 +284,7 @@ describe('ACIR simulator', () => {
const txContext = new TxContext(false, false, true, contractDeploymentData);

it('child function should be callable', async () => {
const abi = ChildAbi.functions.find(f => f.name === 'value') as unknown as FunctionAbi;
const abi = ChildAbi.functions.find(f => f.name === 'value')!;

const txRequest = new TxRequest(
AztecAddress.random(),
Expand All @@ -295,8 +301,8 @@ describe('ACIR simulator', () => {
});

it('parent should call child', async () => {
const childAbi = ChildAbi.functions.find(f => f.name === 'value') as unknown as FunctionAbi;
const parentAbi = ParentAbi.functions.find(f => f.name === 'entryPoint') as unknown as FunctionAbi;
const childAbi = ChildAbi.functions.find(f => f.name === 'value')!;
const parentAbi = ParentAbi.functions.find(f => f.name === 'entryPoint')!;
const childAddress = AztecAddress.random();
const childSelector = Buffer.alloc(4, 1); // should match the call

Expand Down
1 change: 1 addition & 0 deletions yarn-project/acir-simulator/src/public/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class PublicExecution {
notifyCreatedNote: notAvailable,
notifyNullifiedNote: notAvailable,
callPrivateFunction: notAvailable,
viewNotesPage: notAvailable,
storageRead: async ([slot]) => {
const storageSlot = fromACVMField(slot);
const value = await stateActions.read(storageSlot);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/acir-simulator/src/public/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('ACIR public execution simulator', () => {
describe('mint', () => {
it('should run the mint function', async () => {
const contractAddress = AztecAddress.random();
const abi = PublicTokenContractAbi.functions.find(f => f.name === 'mint') as FunctionAbi;
const abi = PublicTokenContractAbi.functions.find(f => f.name === 'mint')!;
const functionData = new FunctionData(Buffer.alloc(4), false, false);
const args = encodeArguments(abi, [140, recipient], false);

Expand Down Expand Up @@ -83,7 +83,7 @@ describe('ACIR public execution simulator', () => {

beforeEach(() => {
contractAddress = AztecAddress.random();
abi = PublicTokenContractAbi.functions.find(f => f.name === 'transfer') as FunctionAbi;
abi = PublicTokenContractAbi.functions.find(f => f.name === 'transfer')!;
functionData = new FunctionData(Buffer.alloc(4), false, false);
args = encodeArguments(abi, [140, recipient], false);
sender = AztecAddress.random();
Expand Down
27 changes: 15 additions & 12 deletions yarn-project/aztec-rpc/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DBOracle, NoteLoadOracleInputs } from '@aztec/acir-simulator';
import { DBOracle } from '@aztec/acir-simulator';
import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js';
import { FunctionAbi } from '@aztec/noir-contracts';
import { ContractDataOracle } from '../contract_data_oracle/index.js';
Expand All @@ -21,18 +21,21 @@ export class SimulatorOracle implements DBOracle {
return this.keyPair.getPrivateKey();
}

async getNotes(contractAddress: AztecAddress, storageSlot: Fr, n: number): Promise<NoteLoadOracleInputs[]> {
async getNotes(contractAddress: AztecAddress, storageSlot: Fr, limit: number, offset: number) {
const noteDaos = await this.db.getTxAuxData(contractAddress, storageSlot);
return Promise.all(
noteDaos.slice(0, n).map(async noteDao => {
const path = await this.node.getDataTreePath(noteDao.index);
return {
preimage: noteDao.notePreimage.items,
siblingPath: path.data.map(buf => Fr.fromBuffer(buf)),
index: noteDao.index,
};
}),
);
return {
count: noteDaos.length,
notes: await Promise.all(
noteDaos.slice(offset, offset + limit).map(async noteDao => {
const path = await this.node.getDataTreePath(noteDao.index);
return {
preimage: noteDao.notePreimage.items,
siblingPath: path.data.map(buf => Fr.fromBuffer(buf)),
index: noteDao.index,
};
}),
),
};
}

async getFunctionABI(contractAddress: AztecAddress, functionSelector: Buffer): Promise<FunctionAbi> {
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/noir-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
```
curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
```
- Install the noir branch needed for A3 backend (`arv/call_stack_item_oracle` at the moment)
- Install the noir branch needed for A3 backend (`joss/ssa-2-brillig-plus-hacks` at the moment)
```
noirup --branch arv/call_stack_item_oracle
```
Expand All @@ -21,4 +21,4 @@

### Examples folder

The examples folder has friendly ABIs to be consumed from aztec3-client. They can be generated from the nargo build output using the `scripts/copy_output.ts`.
The examples folder has friendly ABIs to be consumed from aztec3-client. They can be generated from the nargo build output using the `scripts/copy_output.ts`.
7 changes: 5 additions & 2 deletions yarn-project/noir-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
"formatting:fix": "run -T prettier -w ./src",
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests",
"noir:build:test": "cd src/contracts/test_contract && nargo compile main --contracts",
"noir:build:zk_token": "cd src/contracts/zk_token_contract && nargo compile main --contracts",
"noir:build": "yarn noir:build:test && yarn noir:build:parent && yarn noir:build:child && yarn noir:build:zk_token && yarn noir:build:pub_token",
"noir:build:test": "(cd src/contracts/test_contract && nargo compile main --contracts) && ts-node --esm src/scripts/copy_output.ts test",
"noir:build:parent": "(cd src/contracts/parent_contract && nargo compile main --contracts) && ts-node --esm src/scripts/copy_output.ts parent",
"noir:build:child": "(cd src/contracts/child_contract && nargo compile main --contracts) && ts-node --esm src/scripts/copy_output.ts child",
"noir:build:zk_token": "(cd src/contracts/zk_token_contract && nargo compile main --contracts) && ts-node --esm src/scripts/copy_output.ts zk_token",
"noir:build:pub_token": "(cd src/contracts/public_token_contract && nargo compile main --contracts) && ts-node --esm src/scripts/copy_output.ts public_token"
},
"inherits": [
Expand Down
Loading