Skip to content

Commit

Permalink
chore: Use Noir javascript packages to execute the private kernel cir…
Browse files Browse the repository at this point in the history
…cuit init using the typescript generated types (#2763)
  • Loading branch information
kevaundray authored Oct 11, 2023
1 parent c6c8e4b commit de628ee
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 155 deletions.
7 changes: 7 additions & 0 deletions yarn-project/circuits.js/src/structs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ export * from '@aztec/foundation/eth-address';
export * from '@aztec/foundation/fields';
export * from '@aztec/foundation/aztec-address';
export { FunctionSelector } from '@aztec/foundation/abi';

// TODO(Kev): This is only exported so that privateKernelInit in noir-private-kernel
// does not need to manually initialize its instance.
// This is not great as we are exporting from tests.
// Its okay for now and before merging into master, we should
// remove this line.
export { makePrivateKernelInputsInit } from '../tests/factories.js';
1 change: 1 addition & 0 deletions yarn-project/noir-private-kernel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@noir-lang/acvm_js": "^0.28.0",
"@noir-lang/backend_barretenberg": "^0.7.10",
"@noir-lang/noir_js": "^0.16.0",
"@noir-lang/noirc_abi": "^0.16.0",
"tslib": "^2.4.0"
},
"devDependencies": {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
The typescript binding generator has a bug when we use type aliases because
the abi says that they have the same struct path.

For example:



struct Generic<N> {
x : [Field; N]
}

struct Concrete {
gen2 : Generic<2>,
gen4 : Generic<4>,

}

fn main(input: Concrete) -> pub Field {
0
}


The following code will generate the json:

{"hash":17271335012890464242,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"input","type":{"kind":"struct","path":"Concrete","fields":[{"name":"gen2","type":{"kind":"struct","path":"Generic","fields":[{"name":"x","type":{"kind":"array","length":2,"type":{"kind":"field"}}}]}},{"name":"gen4","type":{"kind":"struct","path":"Generic","fields":[{"name":"x","type":{"kind":"array","length":4,"type":{"kind":"field"}}}]}}]},"visibility":"private"}],"param_witnesses":{"input":[1,2,3,4,5,6]},"return_type":{"kind":"field"},"return_witnesses":[7]},"bytecode":"H4sIAAAAAAAA/6WPSwqAMAxE69/jJE3SJjuvYrG9/xEsqFDQnQ8egVmEmdU517k3T7bdlyAw5+gzEu7gLakASwqKiqJyeCXKyhotWQRDpoxFjApcLM0v+MncdOyrQ3WsTtX5Y8PSZCeMnX6J8AAAAA=="}

And subsequently generate this typescript file:

export type FixedLengthArray<T, L extends number> = L extends 0 ? never[]: T[] & { length: L }

export type Field = string;

export interface Generic {
x: FixedLengthArray<Field, 2>;
}



export interface Concrete {
gen2: Generic;
gen4: Generic;
}

export interface ReturnType {
value: Field;
}

export interface InputType {
input: Concrete;
}

----

The important thing to notice is that there is one Generic and it gets instantiated with
the length of the first parameter.

We can go two ways with this, either we end up with something like:

export interface Generic<N extends number> {
x: FixedLengthArray<Field, N>;
}

export interface Concrete {
gen2: Generic<2>;
gen4: Generic<4>;
}

or we do something like:

export interface Generic2 {
x: FixedLengthArray<Field, 2>;
}
export interface Generic4 {
x: FixedLengthArray<Field, 2>;
}

export interface Concrete {
gen2: Generic2;
gen4: Generic4;
}

First seems to have better devex and less copy pasting but requires more complicated code.
Perhaps this can be aided by the compiler, if we save this information before monomorphization
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::utils::arrays::is_empty_field;
use dep::aztec::constants_gen::{
CONTRACT_TREE_HEIGHT,
FUNCTION_TREE_HEIGHT,
};

struct MembershipWitness<N> {
leaf_index : Field,
Expand All @@ -10,3 +14,17 @@ impl<N> MembershipWitness<N> {
is_empty_field([self.leaf_index]) && is_empty_field(self.sibling_path)
}
}

// TODO(Kev): Instead of doing `MembershipWitness<FUNCTION_TREE_HEIGHT>` we are forced
// to do this new struct because the typescript bindings generator
// does not have logic to monomorphize these properly. See the file named
// `typechain-type-alias` in the folder `bug-collecting-crate`
struct FunctionLeafMembershipWitness{
leaf_index : Field,
sibling_path : [Field; FUNCTION_TREE_HEIGHT]
}

struct ContractLeafMembershipWitness{
leaf_index : Field,
sibling_path : [Field; CONTRACT_TREE_HEIGHT]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use dep::aztec::constants_gen::{
PRIVATE_DATA_TREE_HEIGHT
};
use crate::abis::call_stack_item::PrivateCallStackItem;
use crate::abis::membership_witness::MembershipWitness;
use crate::abis::membership_witness::{ContractLeafMembershipWitness,FunctionLeafMembershipWitness};

type ReadRequestMembershipWitnessPrivateData = ReadRequestMembershipWitness<PRIVATE_DATA_TREE_HEIGHT>;

Expand All @@ -22,8 +22,8 @@ struct PrivateCallData {
proof : Proof,
vk : VerificationKey,

function_leaf_membership_witness : MembershipWitness<FUNCTION_TREE_HEIGHT>,
contract_leaf_membership_witness : MembershipWitness<CONTRACT_TREE_HEIGHT>,
function_leaf_membership_witness : FunctionLeafMembershipWitness,
contract_leaf_membership_witness : ContractLeafMembershipWitness,

read_request_membership_witnesses : [ReadRequestMembershipWitnessPrivateData; MAX_READ_REQUESTS_PER_CALL],

Expand Down
19 changes: 5 additions & 14 deletions yarn-project/noir-private-kernel/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
import { makePrivateKernelInputsInit } from '@aztec/circuits.js';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';

import { WitnessMap, executeCircuit } from '@noir-lang/acvm_js';

import { PrivateKernelInitArtifact } from './index.js';
import { executeInit } from './index.js';

describe('Private kernel', () => {
let logger: DebugLogger;
beforeAll(() => {
logger = createDebugLogger('noir-private-kernel');
});

it('Executes private kernel init circuit with all zeroes', async () => {
it('Executes private kernel init circuit with abi all zeroes (does not crash)', async () => {
logger('Initialized Noir instance with private kernel init circuit');

const decodedBytecode = Buffer.from(PrivateKernelInitArtifact.bytecode, 'base64');
const numWitnesses = 1811; // The number of input witnesses in the private kernel init circuit
const initialWitness: WitnessMap = new Map();
for (let i = 1; i <= numWitnesses; i++) {
initialWitness.set(i, '0x00');
}

const _witnessMap = await executeCircuit(decodedBytecode, initialWitness, () => {
throw Error('unexpected oracle during execution');
});
const kernelInputs = makePrivateKernelInputsInit();
const _kernelOutputs = await executeInit(kernelInputs);

logger('Executed private kernel init circuit with all zeroes');
});
Expand Down
58 changes: 53 additions & 5 deletions yarn-project/noir-private-kernel/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { KernelCircuitPublicInputs, PrivateKernelInputsInit } from '@aztec/circuits.js';
import { NoirCompiledCircuit } from '@aztec/noir-compiler';

import { executeCircuit } from '@noir-lang/acvm_js';
import { abiDecode, abiEncode } from '@noir-lang/noirc_abi';

import PrivateKernelInitJson from './target/private_kernel_init.json' assert { type: 'json' };
import PrivateKernelInnerJson from './target/private_kernel_inner.json' assert { type: 'json' };
import PrivateKernelOrderingJson from './target/private_kernel_ordering.json' assert { type: 'json' };
import { mapPrivateKernelInputsInitToNoir } from './type_conversion.js';
import { InputType as InitInputType } from './types/private_kernel_init_types.js';
import { mapKernelCircuitPublicInputsFromNoir, mapPrivateKernelInputsInitToNoir } from './type_conversion.js';
import { InputType as InitInputType, ReturnType } from './types/private_kernel_init_types.js';

// TODO(Tom): This should be exported from noirc_abi
/**
* The decoded inputs from the circuit.
*/
export type DecodedInputs = {
/**
* The inputs to the circuit
*/
inputs: Record<string, any>;
/**
* The return value of the circuit
*/
return_value: any;
};

export const PrivateKernelInitArtifact = PrivateKernelInitJson as NoirCompiledCircuit;

Expand All @@ -18,10 +36,40 @@ export const PrivateKernelOrderingArtifact = PrivateKernelOrderingJson as NoirCo
* @param privateKernelInputsInit - The private kernel inputs.
* @returns The public inputs.
*/
export function executeInit(privateKernelInputsInit: PrivateKernelInputsInit): Promise<KernelCircuitPublicInputs> {
const _params: InitInputType = {
export async function executeInit(
privateKernelInputsInit: PrivateKernelInputsInit,
): Promise<KernelCircuitPublicInputs> {
const params: InitInputType = {
input: mapPrivateKernelInputsInitToNoir(privateKernelInputsInit),
};

throw new Error('Not implemented');
const returnType = await executePrivateKernelInitWithACVM(params);

return mapKernelCircuitPublicInputsFromNoir(returnType);
}

/**
* Executes the init private kernel with the given inputs using the acvm.
*
* Note: we export this for now, so that we can run tests on it.
* We will make this private and just use `executeInit`.
*/
async function executePrivateKernelInitWithACVM(input: InitInputType): Promise<ReturnType> {
const initialWitnessMap = abiEncode(PrivateKernelInitArtifact.abi, input, null);

// Execute the circuit on those initial witness values
//
// Decode the bytecode from base64 since the acvm does not know about base64 encoding
const decodedBytecode = Buffer.from(PrivateKernelInitArtifact.bytecode, 'base64');
//
// Execute the circuit
const _witnessMap = await executeCircuit(decodedBytecode, initialWitnessMap, () => {
throw Error('unexpected oracle during execution');
});

// Decode the witness map into two fields, the return values and the inputs
const decodedInputs: DecodedInputs = abiDecode(PrivateKernelInitArtifact.abi, _witnessMap);

// Cast the inputs as the return type
return decodedInputs.return_value as ReturnType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function abiTypeToTs(type: ABIType): string {
case 'boolean':
return `boolean`;
case 'array':
return `${abiTypeToTs(type.type)}[]`;
return `FixedLengthArray<${abiTypeToTs(type.type)}, ${type.length}>`;
case 'struct':
return getLastComponentOfPath(type.path);
case 'field':
Expand Down Expand Up @@ -162,13 +162,11 @@ function generateTsInterface(abiObj: NoirFunctionAbi): string {
//
if (abiObj.return_type != null) {
result += generateStructInterfaces(abiObj.return_type, outputStructs);
result += `export interface ReturnType {\n`;
result += ` value: ${abiTypeToTs(abiObj.return_type)};\n`;
result += `}\n\n`;
result += `export type ReturnType = ${abiTypeToTs(abiObj.return_type)};\n`;
}

// Generating Input type
result += 'export interface InputType {\n';
result += '\nexport interface InputType {\n';
for (const param of abiObj.parameters) {
result += ` ${param.name}: ${abiTypeToTs(param.type)};\n`;
}
Expand All @@ -180,7 +178,17 @@ function generateTsInterface(abiObj: NoirFunctionAbi): string {
primitiveTypeAliases += `\nexport type ${value.aliasName} = ${value.tsType};`;
}

return `/* Autogenerated file, do not edit! */\n\n/* eslint-disable */\n` + primitiveTypeAliases + '\n' + result;
const fixedLengthArray =
'\nexport type FixedLengthArray<T, L extends number> = L extends 0 ? never[]: T[] & { length: L }';

return (
`/* Autogenerated file, do not edit! */\n\n/* eslint-disable */\n` +
fixedLengthArray +
'\n' +
primitiveTypeAliases +
'\n' +
result
);
}

const circuits = ['private_kernel_init', 'private_kernel_inner', 'private_kernel_ordering'];
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading

0 comments on commit de628ee

Please sign in to comment.