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: improve noir-contracts.js codegen #4789

Merged
merged 9 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion yarn-project/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ RUN cd l1-artifacts && ./scripts/generate-artifacts.sh && rm -rf /usr/src/l1-con
# This is actually our code generation tool. Needed to build contract typescript wrappers.
RUN yarn workspace @aztec/noir-compiler build
# Generates typescript wrappers.
RUN yarn workspace @aztec/noir-contracts.js build:contracts
RUN yarn workspace @aztec/noir-contracts.js build
# We need to build accounts as it needs to copy in account contracts from noir-contracts.
RUN yarn workspace @aztec/accounts build:copy-contracts
RUN yarn workspace @aztec/protocol-contracts build:copy-contracts
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ echo "Building noir compiler..."
yarn workspace @aztec/noir-compiler build
# Builds noir contracts (TODO: move this stage pre yarn-project). Generates typescript wrappers.
echo "Building contracts from noir-contracts..."
yarn workspace @aztec/noir-contracts.js build:contracts
yarn workspace @aztec/noir-contracts.js build
# Bundle compiled contracts into other packages
echo "Copying account contracts..."
yarn workspace @aztec/accounts build:copy-contracts
Expand Down
49 changes: 47 additions & 2 deletions yarn-project/noir-compiler/src/cli/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
/* eslint-disable no-console */
import { loadContractArtifact } from '@aztec/types/abi';

import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs';
import crypto from 'crypto';
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs';
import path from 'path';

import { generateNoirContractInterface } from '../contract-interface-gen/noir.js';
import { generateTypescriptContractInterface } from '../contract-interface-gen/typescript.js';

const cacheFilePath = './codegenCache.json';
let cache: Record<string, string> = {};

/** Generate code options */
type GenerateCodeOptions = { /** Typescript */ ts?: boolean; /** Noir */ nr?: boolean };

/**
* Generates Noir interface or Typescript interface for a folder or single file from a Noir compilation artifact.
*/
export function generateCode(outputPath: string, fileOrDirPath: string, opts: GenerateCodeOptions = {}) {
readCache();
const stats = statSync(fileOrDirPath);

if (stats.isDirectory()) {
const files = readdirSync(fileOrDirPath).filter(file => file.endsWith('.json') && !file.startsWith('debug_'));
const files = readdirSync(fileOrDirPath, { recursive: true, encoding: 'utf-8' }).filter(
file => file.endsWith('.json') && !file.startsWith('debug_'),
);
for (const file of files) {
const fullPath = path.join(fileOrDirPath, file);
generateFromNoirAbi(outputPath, fullPath, opts);
}
} else if (stats.isFile()) {
generateFromNoirAbi(outputPath, fileOrDirPath, opts);
}
writeCache();
}

/**
* Generates Noir interface or Typescript interface for a single file Noir compilation artifact.
*/
function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts: GenerateCodeOptions = {}) {
const contractName = path.basename(noirAbiPath);
const currentHash = generateFileHash(noirAbiPath);

if (isCacheValid(contractName, currentHash)) {
console.log(`${contractName} has not changed. Skipping generation.`);
return;
}

const contract = JSON.parse(readFileSync(noirAbiPath, 'utf8'));
const aztecAbi = loadContractArtifact(contract);
const { nr, ts } = opts;
Expand All @@ -52,4 +69,32 @@ function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts: Gene
const tsWrapper = generateTypescriptContractInterface(aztecAbi, relativeArtifactPath);
writeFileSync(`${outputPath}/${aztecAbi.name}.ts`, tsWrapper);
}
updateCache(contractName, currentHash);
}

function generateFileHash(filePath: string) {
const fileBuffer = readFileSync(filePath);
const hashSum = crypto.createHash('sha256');
hashSum.update(fileBuffer);
const hex = hashSum.digest('hex');
return hex;
}

function readCache(): void {
if (existsSync(cacheFilePath)) {
const cacheRaw = readFileSync(cacheFilePath, 'utf8');
cache = JSON.parse(cacheRaw);
}
}

function writeCache(): void {
writeFileSync(cacheFilePath, JSON.stringify(cache, null, 2), 'utf8');
}

function isCacheValid(contractName: string, currentHash: string): boolean {
return cache[contractName] === currentHash;
}

function updateCache(contractName: string, hash: string): void {
cache[contractName] = hash;
}
1 change: 1 addition & 0 deletions yarn-project/noir-contracts.js/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target/
/src
artifacts/
codegenCache.json
6 changes: 3 additions & 3 deletions yarn-project/noir-contracts.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
"./*": "./dest/src/*.js"
},
"scripts": {
"build": "yarn clean && yarn build:contracts",
"build": "yarn clean && yarn build:contracts && yarn formatting:fix",
"build:dev": "tsc -b --watch",
"clean": "rm -rf .tsbuildinfo ./artifacts",
"clean": "rm -rf .tsbuildinfo ./artifacts ./codegenCache.json",
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests",
Expand Down Expand Up @@ -49,4 +49,4 @@
"engines": {
"node": ">=18"
}
}
}
4 changes: 2 additions & 2 deletions yarn-project/noir-contracts.js/package.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"scripts": {
"build": "yarn clean && yarn build:contracts",
"build": "yarn clean && yarn build:contracts && yarn formatting:fix",
"build:contracts": "./scripts/generate-types.sh",
"clean": "rm -rf .tsbuildinfo ./artifacts"
"clean": "rm -rf .tsbuildinfo ./artifacts ./codegenCache.json"
}
}
25 changes: 11 additions & 14 deletions yarn-project/noir-contracts.js/scripts/generate-types.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ set -euo pipefail
OUT_DIR="./src"
INDEX="$OUT_DIR/index.ts"

rm -rf $OUT_DIR && mkdir -p $OUT_DIR
mkdir -p $OUT_DIR

#
# Check for .json files existence
if ! ls ../../noir-projects/noir-contracts/target/*.json >/dev/null 2>&1; then
echo "Error: No .json files found in noir-contracts/target folder."
echo "Make sure noir-contracts is built before running this script."
exit 1
fi

# Generate index.ts header.
echo "// Auto generated module - do not edit!" >$INDEX
# Generate index.ts header
echo "// Auto generated module - do not edit!" >"$INDEX"

# Ensure the artifacts directory exists
mkdir -p artifacts
Expand All @@ -25,16 +25,13 @@ for ABI in $(find ../../noir-projects/noir-contracts/target -maxdepth 1 -type f

# Copy the JSON file to the artifacts folder
cp "$ABI" "artifacts/$filename"
done

# Generate the contract name for referencing in the codegen command and index
CONTRACT=$(jq -r .name "artifacts/$filename")

echo "Creating types for $CONTRACT using artifacts/$filename..."
node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --ts "artifacts/$filename"
# Generate types for the contracts
node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --ts artifacts

# Add contract import/export to index.ts.
echo "export * from './${CONTRACT}.js';" >>$INDEX
# Append exports for each generated TypeScript file to index.ts
find "$OUT_DIR" -maxdepth 1 -type f -name '*.ts' ! -name 'index.ts' | while read -r TS_FILE; do
CONTRACT_NAME=$(basename "$TS_FILE" .ts) # Remove the .ts extension to get the contract name
echo "export * from './${CONTRACT_NAME}.js';" >>"$INDEX"
done

echo "Formatting..."
yarn formatting:fix
Loading