Skip to content

Commit

Permalink
chore: improve noir-contracts.js codegen (#4789)
Browse files Browse the repository at this point in the history
- load all noir files in codegen script
- utilize basic caching
Fixes: #4707
  • Loading branch information
spypsy authored Feb 28, 2024
1 parent 821c25d commit d367cc4
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 23 deletions.
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

0 comments on commit d367cc4

Please sign in to comment.