Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// GENERATED FILE - DO NOT EDIT
//
// Written by `yarn-project/standard-contracts/src/scripts/generate_interfaces.ts` from the compiled
// `AuthRegistry` artifact. Regenerate with
// `yarn workspace @aztec/standard-contracts run regen:standard-interfaces`.
//
// The selectors below are derived via `comptime { FunctionSelector::from_signature(...) }` at
// Noir compile time, with the signature string emitted from the artifact's parameter list. This
// keeps the wrapper in lockstep with whatever the `#[aztec]` macro generates for the real
// contract; any drift between the contract's external signatures and this file fails the
// `generate_interfaces.test.ts` freshness gate.

use crate::context::calls::{PublicCall, PublicStaticCall};
use crate::protocol::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::ToField};

/// Hand-written interface stub for the `AuthRegistry` canonical contract.
///
/// The `AuthRegistry` contract records public authentication witnesses
/// (`set_authorized`, `set_reject_all`) and atomically consumes them at
/// the consumer (`consume`). Consumer code in aztec-nr's `authwit::public`
/// module calls into the registry via this interface.
///
/// The selectors are derived with `comptime { FunctionSelector::from_signature(...) }`,
/// which matches exactly what the `#[aztec]` macro generates for the real contract.
pub struct AuthRegistryInterface {
pub target_contract: AztecAddress,
}
Expand Down
35 changes: 19 additions & 16 deletions noir-projects/aztec-nr/aztec/src/public_checks_interface.nr
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
// GENERATED FILE - DO NOT EDIT
//
// Written by `yarn-project/standard-contracts/src/scripts/generate_interfaces.ts` from the compiled
// `PublicChecks` artifact. Regenerate with
// `yarn workspace @aztec/standard-contracts run regen:standard-interfaces`.
//
// The selectors below are derived via `comptime { FunctionSelector::from_signature(...) }` at
// Noir compile time, with the signature string emitted from the artifact's parameter list. This
// keeps the wrapper in lockstep with whatever the `#[aztec]` macro generates for the real
// contract; any drift between the contract's external signatures and this file fails the
// `generate_interfaces.test.ts` freshness gate.

use crate::context::calls::PublicStaticCall;
use crate::protocol::abis::function_selector::FunctionSelector;
use crate::protocol::address::AztecAddress;
use crate::protocol::{abis::function_selector::FunctionSelector, address::AztecAddress};

/// Hand-written interface stub for the `PublicChecks` standard contract.
///
/// The `PublicChecks` contract exposes two view functions that can be enqueued
/// from private context via `enqueue_view_incognito` to assert timestamp or
/// block-number constraints without revealing the calling contract address.
///
/// The selectors are derived with `comptime { FunctionSelector::from_signature(...) }`,
/// which matches exactly what the `#[aztec]` macro generates for the real contract.
pub struct PublicChecksInterface {
pub target_contract: AztecAddress,
}
Expand All @@ -19,22 +22,22 @@ impl PublicChecksInterface {
Self { target_contract }
}

pub fn check_timestamp(self, operation: u8, value: u64) -> PublicStaticCall<15, 2, ()> {
let selector = comptime { FunctionSelector::from_signature("check_timestamp(u8,u64)") };
pub fn check_block_number(self, operation: u8, value: u32) -> PublicStaticCall<18, 2, ()> {
let selector = comptime { FunctionSelector::from_signature("check_block_number(u8,u32)") };
PublicStaticCall::new(
self.target_contract,
selector,
"check_timestamp",
"check_block_number",
[operation as Field, value as Field],
)
}

pub fn check_block_number(self, operation: u8, value: u32) -> PublicStaticCall<18, 2, ()> {
let selector = comptime { FunctionSelector::from_signature("check_block_number(u8,u32)") };
pub fn check_timestamp(self, operation: u8, value: u64) -> PublicStaticCall<15, 2, ()> {
let selector = comptime { FunctionSelector::from_signature("check_timestamp(u8,u64)") };
PublicStaticCall::new(
self.target_contract,
selector,
"check_block_number",
"check_timestamp",
[operation as Field, value as Field],
)
}
Expand Down
1 change: 1 addition & 0 deletions yarn-project/standard-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"generate": "yarn generate:data",
"generate:cleanup-artifacts": "node --no-warnings --loader @swc-node/register/esm src/scripts/cleanup_artifacts.ts",
"generate:data": "node --no-warnings --loader @swc-node/register/esm src/scripts/generate_data.ts",
"regen:standard-interfaces": "node --no-warnings --loader @swc-node/register/esm src/scripts/generate_interfaces.ts",
"build:dev": "../scripts/tsc.sh --watch",
"build:ts": "../scripts/tsc.sh",
"clean": "rm -rf ./dest .tsbuildinfo ./artifacts",
Expand Down
51 changes: 51 additions & 0 deletions yarn-project/standard-contracts/src/scripts/generate_interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// CLI entry for regenerating the autogenerated Noir interface stubs that wrap the standard
// `AuthRegistry` and `PublicChecks` contracts' external functions.
//
// Reads the compiled artifacts (produced by phase 1 of
// `noir-projects/noir-contracts/bootstrap.sh`), renders each `.gen.nr` deterministically from
// the artifact's parameter list, and writes the result back. The freshness test in
// `../standard-interfaces/generate_interfaces.test.ts` re-runs this renderer and asserts
// byte-equality against what is committed.
import type { NoirCompiledContract } from '@aztec/stdlib/noir';

import { promises as fs } from 'node:fs';
import path from 'node:path';

import {
ALL_INTERFACES,
type InterfaceSpec,
renderInterfaceFile,
} from '../standard-interfaces/generate_interfaces.js';

async function writeIfChanged(filePath: string, content: string): Promise<void> {
try {
const existing = await fs.readFile(filePath, 'utf8');
if (existing === content) {
return;
}
} catch (err: any) {
if (err.code !== 'ENOENT') {
throw err;
}
}
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, content);
}

async function regenerateInterface(spec: InterfaceSpec): Promise<void> {
const artifactRaw = await fs.readFile(spec.artifactPath, 'utf8');
const artifact = JSON.parse(artifactRaw) as NoirCompiledContract;
const content = renderInterfaceFile(spec, artifact);
await writeIfChanged(spec.outputPath, content);
process.stdout.write(`regenerated ${spec.interfaceName} -> ${spec.outputPath}\n`);
}

async function main() {
for (const spec of ALL_INTERFACES) {
await regenerateInterface(spec);
}
}

if (import.meta.url === `file://${process.argv[1]}`) {
await main();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { NoirCompiledContract } from '@aztec/stdlib/noir';

import { promises as fs } from 'node:fs';

import { ALL_INTERFACES, renderInterfaceFile } from './generate_interfaces.js';

const REGEN_HINT =
'standard interface stubs are stale; run `yarn workspace @aztec/standard-contracts run regen:standard-interfaces` and commit the result.';

describe('standard interface stub freshness', () => {
for (const spec of ALL_INTERFACES) {
describe(spec.interfaceName, () => {
let artifactExists = false;
beforeAll(async () => {
artifactExists = await fs
.access(spec.artifactPath)
.then(() => true)
.catch(() => false);
});

it('on-disk .gen.nr matches the freshly-rendered output', async () => {
if (!artifactExists) {
// Artifact is produced by `./bootstrap.sh build` (or `nargo compile` +
// `bb aztec_process` for the noir-contracts package). Skip with a clear message
// rather than fail when the artifact has not been built yet — the dedicated CI
// job that runs this test ensures the artifact is on disk before invoking jest.
console.warn(`Skipping ${spec.interfaceName}: ${spec.artifactPath} not found.`);
return;
}
const artifact = JSON.parse(await fs.readFile(spec.artifactPath, 'utf8')) as NoirCompiledContract;
const expected = renderInterfaceFile(spec, artifact);
const actual = await fs.readFile(spec.outputPath, 'utf8');

if (actual !== expected) {
throw new Error(REGEN_HINT);
}
});

it('render is deterministic for the same artifact', async () => {
if (!artifactExists) {
return;
}
const artifact = JSON.parse(await fs.readFile(spec.artifactPath, 'utf8')) as NoirCompiledContract;
expect(renderInterfaceFile(spec, artifact)).toEqual(renderInterfaceFile(spec, artifact));
});
});
}
});
Loading
Loading