Skip to content

Commit

Permalink
feat: noir brillig compiler (#316)
Browse files Browse the repository at this point in the history
* wip: new noir compiler

* wip

* fix(noir): worked around new compiler issues

* fix: remove unused returns

* wip: public function integration

* chore: completed build system for noir contracts

* chore: removed old jsons

* style: removed unused as FunctionAbi

* fix: flatten flow for public transfer

* style: fix json styles

* chore: mocked vks

* fix: public transfer

* chore: enforce alphabetical abi fn ordering

* fix: remove workaround for oracle simple returns

* fix: remove contract address from get 2 notes orac

* feat(sim): support for fetching n notes

* fix(noir): remove branching workaround

* feat: updated notes oracle with pagination

* refator: removed unused fns
  • Loading branch information
sirasistant authored Apr 21, 2023
1 parent 51199f2 commit cd2fca3
Show file tree
Hide file tree
Showing 32 changed files with 418 additions and 271 deletions.
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

0 comments on commit cd2fca3

Please sign in to comment.