Skip to content

Commit

Permalink
feat: add noir_codegen package (#3392)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Nov 14, 2023
1 parent 866b62b commit 2f45b29
Show file tree
Hide file tree
Showing 18 changed files with 412 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"tooling/noir_js_types",
"tooling/noirc_abi_wasm",
"tooling/noir_js",
"tooling/noir_codegen",
"tooling/noir_js_backend_barretenberg",
"acvm-repo/acvm_js",
"release-tests",
Expand Down
9 changes: 7 additions & 2 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"path": "compiler/wasm/package.json",
"jsonpath": "$.version"
},
{
"type": "json",
"path": "tooling/noir_codegen/package.json",
"jsonpath": "$.version"
},
{
"type": "json",
"path": "tooling/noir_js/package.json",
Expand All @@ -45,8 +50,8 @@
"jsonpath": "$.version"
}
]
},
"acvm-repo" : {
},
"acvm-repo": {
"release-type": "simple",
"package-name": "acvm",
"component": "acvm",
Expand Down
2 changes: 2 additions & 0 deletions tooling/noir_codegen/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
lib
3 changes: 3 additions & 0 deletions tooling/noir_codegen/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ["../../.eslintrc.js"],
};
4 changes: 4 additions & 0 deletions tooling/noir_codegen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
crs
lib

!test/*/target
11 changes: 11 additions & 0 deletions tooling/noir_codegen/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"require": "ts-node/register",
"loader": "ts-node/esm",
"extensions": [
"ts",
"cjs"
],
"spec": [
"test/**/*.test.ts*"
]
}
4 changes: 4 additions & 0 deletions tooling/noir_codegen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

## Acknowledgements

`noir-codegen` repurposes the CLI code from https://github.com/dethcrypto/TypeChain, used under the MIT license.
53 changes: 53 additions & 0 deletions tooling/noir_codegen/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@noir-lang/noir_codegen",
"collaborators": [
"The Noir Team <team@noir-lang.org>"
],
"version": "0.18.0",
"packageManager": "yarn@3.5.1",
"license": "(MIT OR Apache-2.0)",
"type": "module",
"dependencies": {
"@noir-lang/types": "workspace:*",
"glob": "^10.3.10",
"lodash": "^4.17.21",
"ts-command-line-args": "^2.5.1"
},
"files": [
"lib",
"package.json"
],
"source": "src/index.ts",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"bin": {
"noir-codegen": "lib/main.js"
},
"scripts": {
"dev": "tsc-multi --watch",
"build": "tsc",
"test": "ts-node --esm src/main.ts ./test/assert_lt/target/** --out-dir ./test/codegen && yarn test:node && rm -rf ./test/codegen",
"test:node": "mocha --timeout 25000 --exit --config ./.mocharc.json",
"prettier": "prettier 'src/**/*.ts'",
"prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
"lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0",
"nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json",
"publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish",
"clean": "rm -rf ./lib"
},
"devDependencies": {
"@noir-lang/noir_js": "workspace:*",
"@types/chai": "^4",
"@types/lodash": "^4",
"@types/mocha": "^10.0.1",
"@types/node": "^20.6.2",
"@types/prettier": "^3",
"chai": "^4.3.8",
"eslint": "^8.50.0",
"eslint-plugin-prettier": "^5.0.0",
"mocha": "^10.2.0",
"prettier": "3.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
}
}
28 changes: 28 additions & 0 deletions tooling/noir_codegen/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CompiledCircuit } from '@noir-lang/types';

const codegenImports = `import { InputMap, InputValue } from "@noir-lang/noirc_abi"
import { Noir } from "@noir-lang/noir_js"`;

const codegenFunction = (
name: string,
compiled_program: CompiledCircuit,
) => `export async function ${name}(args: InputMap): Promise<InputValue> {
const program = new Noir(${JSON.stringify(compiled_program)});
const { returnValue } = await program.execute(args);
return returnValue;
}`;

export const codegen = (programs: [string, CompiledCircuit][]): string => {
const results = [codegenImports];
for (const [name, program] of programs) {
results.push(codegenFunction(name, stripUnwantedFields(program)));
}

return results.join('\n\n');
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function stripUnwantedFields(value: any): CompiledCircuit {
const { abi, bytecode } = value;
return { abi, bytecode };
}
47 changes: 47 additions & 0 deletions tooling/noir_codegen/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#! /usr/bin/env node

import { CompiledCircuit } from '@noir-lang/types';
import fs from 'fs';
import path from 'path';
import { parseArgs } from './parseArgs.js';
import { glob } from './utils/glob.js';
import { codegen } from './index.js';

function main() {
const cliConfig = parseArgs();
const cwd = process.cwd();

const files = getFilesToProcess(cwd, cliConfig.files);
if (files.length === 0) {
throw new Error('No files passed.' + '\n' + `\`${cliConfig.files}\` didn't match any input files in ${cwd}`);
}

const programs = files.map((file_path): [string, CompiledCircuit] => {
const program_name = path.parse(file_path).name;
const file_contents = fs.readFileSync(file_path).toString();
const { abi, bytecode } = JSON.parse(file_contents);

return [program_name, { abi, bytecode }];
});

const result = codegen(programs);

const outputDir = path.resolve(cliConfig.outDir ?? './codegen');
const outputFile = path.join(outputDir, 'index.ts');
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
fs.writeFileSync(outputFile, result);
}

function getFilesToProcess(cwd: string, filesOrPattern: string[]) {
let res = glob(cwd, filesOrPattern);

if (res.length === 0) {
// If there are no files found, but first parameter is surrounded with single quotes, we try again without quotes
const match = filesOrPattern[0].match(/'([\s\S]*)'/)?.[1];
if (match) res = glob(cwd, [match]);
}

return res;
}

main();
64 changes: 64 additions & 0 deletions tooling/noir_codegen/src/parseArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { parse as commandLineArgs } from 'ts-command-line-args';

const DEFAULT_GLOB_PATTERN = './target/**/*.json';

export interface ParsedArgs {
files: string[];
outDir?: string | undefined;
inputDir?: string | undefined;
}

export function parseArgs(): ParsedArgs {
const rawOptions = commandLineArgs<CommandLineArgs>(
{
glob: {
type: String,
defaultOption: true,
multiple: true,
defaultValue: [DEFAULT_GLOB_PATTERN],
description:
'Pattern that will be used to find program artifacts. Remember about adding quotes: noir-codegen "**/*.json".',
},
'out-dir': { type: String, optional: true, description: 'Output directory for generated files.' },
'input-dir': {
type: String,
optional: true,
description:
'Directory containing program artifact files. Inferred as lowest common path of all files if not specified.',
},
help: { type: Boolean, defaultValue: false, alias: 'h', description: 'Prints this message.' },
},
{
helpArg: 'help',
headerContentSections: [
{
content: `\
noir-codegen generates TypeScript wrappers for Noir programs to simplify replicating your Noir logic in JS.`,
},
],
footerContentSections: [
{
header: 'Example Usage',
content: `\
noir-codegen --out-dir app/noir_programs './target/*.json'
You can read more about noir-codegen at {underline https://github.com/noir-lang/noir}.`,
},
],
},
);

return {
files: rawOptions.glob,
outDir: rawOptions['out-dir'],
inputDir: rawOptions['input-dir'],
};
}

interface CommandLineArgs {
glob: string[];
'out-dir'?: string;
'input-dir'?: string;
help: boolean;
}
9 changes: 9 additions & 0 deletions tooling/noir_codegen/src/utils/glob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { sync as globSync } from 'glob';
import _ from 'lodash';
const { flatten, uniq } = _;

export function glob(cwd: string, patternsOrFiles: string[]): string[] {
const matches = patternsOrFiles.map((p) => globSync(p, { ignore: 'node_modules/**', absolute: true, cwd }));

return uniq(flatten(matches));
}
5 changes: 5 additions & 0 deletions tooling/noir_codegen/test/assert_lt/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "assert_lt"
type = "bin"
authors = [""]
[dependencies]
4 changes: 4 additions & 0 deletions tooling/noir_codegen/test/assert_lt/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main(x : u64, y : pub u64) -> pub u64 {
assert(x < y);
x + y
}
1 change: 1 addition & 0 deletions tooling/noir_codegen/test/assert_lt/target/assert_lt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"hash":13834844072603749544,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":{"kind":"integer","sign":"unsigned","width":64},"return_witnesses":[12]},"bytecode":"H4sIAAAAAAAA/+1WUW6DMAx1QksZoGr72jUcAiX8VbvJ0Oj9j7ChJpKbtXw0NpvUWkImUXixn53w3gDgHc6mfh7t/ZGMtR9TU96HeYuHtp36ZjLWfGIzjK7DthsPzjjTue6rcdZOrnX9MA49Dqa1kzl1gz3h2bL7sTDCMhmJbylmTDOT8WEhjXfjH/DcB8u8zwVygWifmL/9lTnWzSWKsxHA3QJf00vlveWvERJIUU4x0eb86aEJppljVox9oO+Py8QTV1Jnw6a85t7vSL8pwvN89j7gd88o8q79Gr2wRt3AeSFz4XvRSyokl5MAtSfgGO2ZCewdsDibLRVrDzIXTMxfqiLIGXPeMdY1gb/Fg8+tznJY50eSGmfB2DNrqciCD+tCRc4X5FNFJmIWnkhu3BL+t4qc8y75aySqIkvGOP9CRWKaGQ0ydUrsgUUVWXlfw4OpyAouVWQN66pITDPDqSJfQaZxuVVkxZhzzVgLTv5uHbDwXhN+vwGywklHPBQAAA=="}
11 changes: 11 additions & 0 deletions tooling/noir_codegen/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { expect } from 'chai';
import { assert_lt } from './codegen/index.js';

it('codegens a callable function', async () => {
const result = await assert_lt({
x: '2',
y: '3',
});

expect(result).to.be.eq('0x05');
});
20 changes: 20 additions & 0 deletions tooling/noir_codegen/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "esnext",
"declaration": true,
"emitDeclarationOnly": false,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./lib",
"esModuleInterop": true,
"resolveJsonModule": true,
"strict": true,
"noImplicitAny": false,
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Loading

0 comments on commit 2f45b29

Please sign in to comment.