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

chore: Use Noir javascript packages to execute the private kernel circuit init using the typescript generated types #2763

Merged
merged 18 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 15 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
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';
Copy link
Contributor Author

@kevaundray kevaundray Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay tech debt at the moment -- until we want to merge into master, without it we will need to manually make an instance of PrivateKernelInputsInit in noir-private-kernel in order to have tests

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 @@ -32,6 +32,7 @@
"@aztec/foundation": "workspace:^",
"@aztec/noir-compiler": "workspace:^",
"@noir-lang/acvm_js": "^0.28.0",
"@noir-lang/noirc_abi": "^0.28.0",
"@noir-lang/backend_barretenberg": "^0.7.10",
"@noir-lang/noir_js": "^0.16.0",
"tslib": "^2.4.0"
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
20 changes: 6 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,20 @@
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 () => {
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');
}
it('Executes private kernel init circuit with abi all zeroes (does not crash)', async () => {
logger('Initialized Noir instance with private kernel init circuit');

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
54 changes: 49 additions & 5 deletions yarn-project/noir-private-kernel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,24 @@ import { NoirCompiledCircuit } from '@aztec/noir-compiler';
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 { mapPrivateKernelInputsInitToNoir,mapKernelCircuitPublicInputsFromNoir } from './type_conversion.js';
import { InputType as InitInputType, ReturnType } from './types/private_kernel_init_types.js';

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

// 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 +34,38 @@ 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,9 @@ 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