diff --git a/yarn-project/accounts/package.json b/yarn-project/accounts/package.json index 2a59abcc1b3..e9c63424550 100644 --- a/yarn-project/accounts/package.json +++ b/yarn-project/accounts/package.json @@ -24,7 +24,7 @@ }, "scripts": { "build": "yarn clean && yarn build:copy-contracts && tsc -b", - "build:copy-contracts": "mkdir -p ./src/artifacts && cp ../noir-contracts/src/SchnorrAccount.json ../noir-contracts/src/EcdsaAccount.json ../noir-contracts/src/SchnorrSingleKeyAccount.json ./src/artifacts", + "build:copy-contracts": "./scripts/copy-contracts.sh", "build:dev": "tsc -b --watch", "build:ts": "tsc -b", "clean": "rm -rf ./dest .tsbuildinfo ./src/artifacts", diff --git a/yarn-project/accounts/package.local.json b/yarn-project/accounts/package.local.json index e502a0f74d0..def45a001a2 100644 --- a/yarn-project/accounts/package.local.json +++ b/yarn-project/accounts/package.local.json @@ -1,7 +1,7 @@ { "scripts": { "build": "yarn clean && yarn build:copy-contracts && tsc -b", - "build:copy-contracts": "mkdir -p ./src/artifacts && cp ../noir-contracts/src/SchnorrAccount.json ../noir-contracts/src/EcdsaAccount.json ../noir-contracts/src/SchnorrSingleKeyAccount.json ./src/artifacts", + "build:copy-contracts": "./scripts/copy-contracts.sh", "build:dev": "tsc -b --watch", "build:ts": "tsc -b", "clean": "rm -rf ./dest .tsbuildinfo ./src/artifacts" diff --git a/yarn-project/accounts/scripts/copy-contracts.sh b/yarn-project/accounts/scripts/copy-contracts.sh new file mode 100755 index 00000000000..8c6b0e75332 --- /dev/null +++ b/yarn-project/accounts/scripts/copy-contracts.sh @@ -0,0 +1,9 @@ +#! /bin/bash +set -euo pipefail +mkdir -p ./src/artifacts + +contracts=(schnorr_account_contract-SchnorrAccount ecdsa_account_contract-EcdsaAccount schnorr_single_key_account_contract-SchnorrSingleKeyAccount) + +for contract in "${contracts[@]}"; do + cp "../noir-contracts/target/$contract.json" ./src/artifacts/${contract#*-}.json +done diff --git a/yarn-project/accounts/src/ecdsa/artifact.ts b/yarn-project/accounts/src/ecdsa/artifact.ts index 05c0aae17c9..4dd2fd60dd6 100644 --- a/yarn-project/accounts/src/ecdsa/artifact.ts +++ b/yarn-project/accounts/src/ecdsa/artifact.ts @@ -1,5 +1,5 @@ -import { ContractArtifact } from '@aztec/aztec.js'; +import { NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js'; import EcdsaAccountContractJson from '../artifacts/EcdsaAccount.json' assert { type: 'json' }; -export const EcdsaAccountContractArtifact = EcdsaAccountContractJson as ContractArtifact; +export const EcdsaAccountContractArtifact = loadContractArtifact(EcdsaAccountContractJson as NoirCompiledContract); diff --git a/yarn-project/accounts/src/schnorr/artifact.ts b/yarn-project/accounts/src/schnorr/artifact.ts index 672b633420e..1d9088bf3ca 100644 --- a/yarn-project/accounts/src/schnorr/artifact.ts +++ b/yarn-project/accounts/src/schnorr/artifact.ts @@ -1,5 +1,5 @@ -import { ContractArtifact } from '@aztec/aztec.js'; +import { NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js'; import SchnorrAccountContractJson from '../artifacts/SchnorrAccount.json' assert { type: 'json' }; -export const SchnorrAccountContractArtifact = SchnorrAccountContractJson as ContractArtifact; +export const SchnorrAccountContractArtifact = loadContractArtifact(SchnorrAccountContractJson as NoirCompiledContract); diff --git a/yarn-project/accounts/src/single_key/artifact.ts b/yarn-project/accounts/src/single_key/artifact.ts index 4bb139b1252..954c5f199e7 100644 --- a/yarn-project/accounts/src/single_key/artifact.ts +++ b/yarn-project/accounts/src/single_key/artifact.ts @@ -1,5 +1,7 @@ -import { ContractArtifact } from '@aztec/aztec.js'; +import { NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js'; import SchnorrSingleKeyAccountContractJson from '../artifacts/SchnorrSingleKeyAccount.json' assert { type: 'json' }; -export const SchnorrSingleKeyAccountContractArtifact = SchnorrSingleKeyAccountContractJson as ContractArtifact; +export const SchnorrSingleKeyAccountContractArtifact = loadContractArtifact( + SchnorrSingleKeyAccountContractJson as NoirCompiledContract, +); diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 560c5923ecd..eb4f59fae84 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -1,6 +1,6 @@ import { L2Block, MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/circuit-types'; import { BlockHeader, CompleteAddress, GrumpkinPrivateKey, PublicKey } from '@aztec/circuits.js'; -import { FunctionArtifact, FunctionDebugMetadata, FunctionSelector } from '@aztec/foundation/abi'; +import { FunctionArtifactWithDebugMetadata, FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; @@ -17,16 +17,6 @@ export class ContractNotFoundError extends Error { } } -/** - * A function artifact with optional debug metadata - */ -export interface FunctionArtifactWithDebugMetadata extends FunctionArtifact { - /** - * Debug metadata for the function. - */ - debug?: FunctionDebugMetadata; -} - /** * The database oracle interface. */ diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index cadb2afec9f..574e4b7b521 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -21,7 +21,13 @@ import { siloCommitment, } from '@aztec/circuits.js/abis'; import { makeContractDeploymentData } from '@aztec/circuits.js/factories'; -import { FunctionArtifact, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; +import { + FunctionArtifact, + FunctionSelector, + encodeArguments, + getFunctionArtifact, + getFunctionArtifactWithSelector, +} from '@aztec/foundation/abi'; import { asyncMap } from '@aztec/foundation/async-map'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; @@ -46,7 +52,7 @@ import { default as levelup } from 'levelup'; import { type MemDown, default as memdown } from 'memdown'; import { getFunctionSelector } from 'viem'; -import { buildL1ToL2Message, getFunctionArtifact, getFunctionArtifactWithSelector } from '../test/utils.js'; +import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { DBOracle } from './db_oracle.js'; import { AcirSimulator } from './simulator.js'; diff --git a/yarn-project/acir-simulator/src/client/private_execution.ts b/yarn-project/acir-simulator/src/client/private_execution.ts index d2d43ca534d..c16fcd83b34 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.ts @@ -1,5 +1,5 @@ import { FunctionData, PrivateCallStackItem } from '@aztec/circuits.js'; -import { decodeReturnValues } from '@aztec/foundation/abi'; +import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -9,7 +9,6 @@ import { extractPrivateCircuitPublicInputs } from '../acvm/deserialize.js'; import { Oracle, acvm, extractCallStack } from '../acvm/index.js'; import { ExecutionError } from '../common/errors.js'; import { ClientExecutionContext } from './client_execution_context.js'; -import { FunctionArtifactWithDebugMetadata } from './db_oracle.js'; import { ExecutionResult } from './execution_result.js'; import { AcirSimulator } from './simulator.js'; diff --git a/yarn-project/acir-simulator/src/client/simulator.test.ts b/yarn-project/acir-simulator/src/client/simulator.test.ts index 406a1292461..b1e0d95a4bc 100644 --- a/yarn-project/acir-simulator/src/client/simulator.test.ts +++ b/yarn-project/acir-simulator/src/client/simulator.test.ts @@ -1,7 +1,7 @@ import { Note } from '@aztec/circuit-types'; import { CompleteAddress } from '@aztec/circuits.js'; import { computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis'; -import { ABIParameterVisibility } from '@aztec/foundation/abi'; +import { ABIParameterVisibility, FunctionArtifactWithDebugMetadata, getFunctionArtifact } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; @@ -9,8 +9,7 @@ import { TokenContractArtifact } from '@aztec/noir-contracts/Token'; import { MockProxy, mock } from 'jest-mock-extended'; -import { getFunctionArtifact } from '../test/utils.js'; -import { DBOracle, FunctionArtifactWithDebugMetadata } from './db_oracle.js'; +import { DBOracle } from './db_oracle.js'; import { AcirSimulator } from './simulator.js'; describe('Simulator', () => { diff --git a/yarn-project/acir-simulator/src/client/simulator.ts b/yarn-project/acir-simulator/src/client/simulator.ts index 0e11e43509d..92430a19969 100644 --- a/yarn-project/acir-simulator/src/client/simulator.ts +++ b/yarn-project/acir-simulator/src/client/simulator.ts @@ -1,7 +1,13 @@ import { AztecNode, FunctionCall, Note, TxExecutionRequest } from '@aztec/circuit-types'; import { CallContext, FunctionData } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { ArrayType, FunctionSelector, FunctionType, encodeArguments } from '@aztec/foundation/abi'; +import { + ArrayType, + FunctionArtifactWithDebugMetadata, + FunctionSelector, + FunctionType, + encodeArguments, +} from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; @@ -12,7 +18,7 @@ import { WasmBlackBoxFunctionSolver, createBlackBoxSolver } from '@noir-lang/acv import { createSimulationError } from '../common/errors.js'; import { PackedArgsCache } from '../common/packed_args_cache.js'; import { ClientExecutionContext } from './client_execution_context.js'; -import { DBOracle, FunctionArtifactWithDebugMetadata } from './db_oracle.js'; +import { DBOracle } from './db_oracle.js'; import { ExecutionNoteCache } from './execution_note_cache.js'; import { ExecutionResult } from './execution_result.js'; import { executePrivateFunction } from './private_execution.js'; diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts index d197ec386fb..71730b411fa 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts @@ -1,5 +1,5 @@ import { FunctionData } from '@aztec/circuits.js'; -import { DecodedReturn, decodeReturnValues } from '@aztec/foundation/abi'; +import { DecodedReturn, FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -8,7 +8,6 @@ import { extractReturnWitness } from '../acvm/deserialize.js'; import { ACVMField, Oracle, acvm, extractCallStack, fromACVMField, toACVMWitness } from '../acvm/index.js'; import { ExecutionError } from '../common/errors.js'; import { AcirSimulator } from '../index.js'; -import { FunctionArtifactWithDebugMetadata } from './db_oracle.js'; import { ViewDataOracle } from './view_data_oracle.js'; /** diff --git a/yarn-project/acir-simulator/src/test/utils.ts b/yarn-project/acir-simulator/src/test/utils.ts index e2e0310d458..916243400e4 100644 --- a/yarn-project/acir-simulator/src/test/utils.ts +++ b/yarn-project/acir-simulator/src/test/utils.ts @@ -1,11 +1,8 @@ import { L1Actor, L1ToL2Message, L2Actor } from '@aztec/circuit-types'; import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; import { computeSecretMessageHash } from '@aztec/circuits.js/abis'; -import { ContractArtifact, FunctionSelector, getFunctionDebugMetadata } from '@aztec/foundation/abi'; import { sha256 } from '@aztec/foundation/crypto'; -import { FunctionArtifactWithDebugMetadata } from '../index.js'; - /** * Test utility function to craft an L1 to L2 message. * @param selector - The cross chain message selector. @@ -39,31 +36,3 @@ export const buildL1ToL2Message = ( 0, ); }; - -export const getFunctionArtifact = ( - artifact: ContractArtifact, - functionName: string, -): FunctionArtifactWithDebugMetadata => { - const functionArtifact = artifact.functions.find(f => f.name === functionName); - if (!functionArtifact) { - throw new Error(`Unknown function ${functionName}`); - } - - const debug = getFunctionDebugMetadata(artifact, functionName); - return { ...functionArtifact, debug }; -}; - -export const getFunctionArtifactWithSelector = ( - artifact: ContractArtifact, - functionSelector: FunctionSelector, -): FunctionArtifactWithDebugMetadata => { - const functionArtifact = artifact.functions.find(f => - functionSelector.equals(FunctionSelector.fromNameAndParameters(f.name, f.parameters)), - ); - if (!functionArtifact) { - throw new Error(`Unknown function ${functionSelector}`); - } - - const debug = getFunctionDebugMetadata(artifact, functionArtifact.name); - return { ...functionArtifact, debug }; -}; diff --git a/yarn-project/aztec.js/package.json b/yarn-project/aztec.js/package.json index 7b6d167eef2..c3e3c461005 100644 --- a/yarn-project/aztec.js/package.json +++ b/yarn-project/aztec.js/package.json @@ -25,8 +25,7 @@ "tsconfig": "./tsconfig.json" }, "scripts": { - "build": "yarn clean && yarn build:copy-contracts && tsc -b && webpack", - "build:copy-contracts": "mkdir -p ./src/account_contract/artifacts && cp ../noir-contracts/src/SchnorrAccount.json ../noir-contracts/src/EcdsaAccount.json ../noir-contracts/src/SchnorrSingleKeyAccount.json ./src/account_contract/artifacts", + "build": "yarn clean && tsc -b && webpack", "build:web": "webpack", "build:dev": "tsc -b --watch", "build:ts": "tsc -b", diff --git a/yarn-project/aztec.js/package.local.json b/yarn-project/aztec.js/package.local.json index db6bd6204c7..e070cdff602 100644 --- a/yarn-project/aztec.js/package.local.json +++ b/yarn-project/aztec.js/package.local.json @@ -1,6 +1,6 @@ { "scripts": { - "build": "yarn clean && yarn build:copy-contracts && tsc -b && webpack", + "build": "yarn clean && tsc -b && webpack", "build:web": "webpack", "build:dev": "tsc -b --watch", "build:ts": "tsc -b", diff --git a/yarn-project/aztec.js/src/api/abi.ts b/yarn-project/aztec.js/src/api/abi.ts index d76502b881d..7e94b7eb30f 100644 --- a/yarn-project/aztec.js/src/api/abi.ts +++ b/yarn-project/aztec.js/src/api/abi.ts @@ -1 +1,3 @@ export { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; +export { loadContractArtifact } from '@aztec/types/abi'; +export { NoirCompiledContract } from '@aztec/types/noir'; diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 524eb778393..14bdbe71f29 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -43,6 +43,7 @@ describe('Contract Class', () => { name: 'bar', functionType: FunctionType.SECRET, isInternal: false, + debugSymbols: '', parameters: [ { name: 'value', @@ -69,6 +70,7 @@ describe('Contract Class', () => { parameters: [], returnTypes: [], bytecode: '0be', + debugSymbols: '', }, { name: 'qux', @@ -91,9 +93,11 @@ describe('Contract Class', () => { }, ], bytecode: '0cd', + debugSymbols: '', }, ], events: [], + fileMap: {}, }; beforeEach(() => { diff --git a/yarn-project/aztec.js/src/contract_deployer/contract_deployer.test.ts b/yarn-project/aztec.js/src/contract_deployer/contract_deployer.test.ts index 8316f8f0a71..6fabd46e2ac 100644 --- a/yarn-project/aztec.js/src/contract_deployer/contract_deployer.test.ts +++ b/yarn-project/aztec.js/src/contract_deployer/contract_deployer.test.ts @@ -19,9 +19,11 @@ describe.skip('Contract Deployer', () => { parameters: [], returnTypes: [], bytecode: '0af', + debugSymbols: '', }, ], events: [], + fileMap: {}, }; const publicKey: PublicKey = Point.random(); diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index d0f2ba7a8a2..ff2378a23bf 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -64,7 +64,6 @@ export { EthAddress, Fr, Fq, - FunctionSelector, GlobalVariables, GrumpkinScalar, Point, @@ -114,7 +113,7 @@ export { NodeInfo } from '@aztec/types/interfaces'; // TODO: These kinds of things have no place on our public api. // External devs will almost certainly have their own methods of doing these things. // If we want to use them in our own "aztec.js consuming code", import them from foundation as needed. -export { ContractArtifact, FunctionArtifact, encodeArguments } from '@aztec/foundation/abi'; +export { encodeArguments } from '@aztec/foundation/abi'; export { sha256 } from '@aztec/foundation/crypto'; export { DebugLogger, createDebugLogger, onLog } from '@aztec/foundation/log'; export { retry, retryUntil } from '@aztec/foundation/retry'; @@ -137,3 +136,4 @@ export { // Here you *can* do `export *` as the granular api defacto exports things explicitly. // This entire index file will be deprecated at some point after we're satisfied. export * from './api/init.js'; +export * from './api/abi.js'; diff --git a/yarn-project/circuit-types/src/contract_dao.test.ts b/yarn-project/circuit-types/src/contract_dao.test.ts index 5cfa8ea7e92..5e8faa4650a 100644 --- a/yarn-project/circuit-types/src/contract_dao.test.ts +++ b/yarn-project/circuit-types/src/contract_dao.test.ts @@ -38,9 +38,11 @@ describe('ContractDao', () => { ], returnTypes: [], bytecode: '0af', + debugSymbols: '', }, ], events: [], + fileMap: {}, }; const dao = new ContractDao(artifact, CompleteAddress.random(), EthAddress.random()); diff --git a/yarn-project/circuit-types/src/contract_dao.ts b/yarn-project/circuit-types/src/contract_dao.ts index 2817c1f2551..79e07af969f 100644 --- a/yarn-project/circuit-types/src/contract_dao.ts +++ b/yarn-project/circuit-types/src/contract_dao.ts @@ -1,7 +1,7 @@ import { CompleteAddress, ContractFunctionDao } from '@aztec/circuits.js'; import { ContractArtifact, - DebugMetadata, + DebugFileMap, EventAbi, FunctionDebugMetadata, FunctionSelector, @@ -46,8 +46,8 @@ export class ContractDao implements ContractArtifact { return this.contractArtifact.events; } - get debug(): DebugMetadata | undefined { - return this.contractArtifact.debug; + get fileMap(): DebugFileMap { + return this.contractArtifact.fileMap; } getFunctionArtifact(selector: FunctionSelector): ContractFunctionDao | undefined { @@ -59,7 +59,8 @@ export class ContractDao implements ContractArtifact { } getFunctionDebugMetadataByName(functionName: string): FunctionDebugMetadata | undefined { - return getFunctionDebugMetadata(this, functionName); + const fn = this.getFunctionArtifactByName(functionName); + return fn && getFunctionDebugMetadata(this, fn); } toBuffer(): Buffer { diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 44336d6caa3..c4d9827e852 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -48,6 +48,7 @@ export const randomContractArtifact = (): ContractArtifact => ({ name: randomBytes(4).toString('hex'), functions: [], events: [], + fileMap: {}, }); export const randomDeployedContract = (): DeployedContract => ({ diff --git a/yarn-project/cli/src/test/mocks.ts b/yarn-project/cli/src/test/mocks.ts index f02cbdb8f75..7a9e662eda6 100644 --- a/yarn-project/cli/src/test/mocks.ts +++ b/yarn-project/cli/src/test/mocks.ts @@ -18,6 +18,7 @@ export const mockContractArtifact: ContractArtifact = { ], returnTypes: [], bytecode: 'constructorBytecode', + debugSymbols: '', }, { name: 'mockFunction', @@ -59,7 +60,9 @@ export const mockContractArtifact: ContractArtifact = { ], returnTypes: [{ kind: 'boolean' }], bytecode: 'mockBytecode', + debugSymbols: '', }, ], events: [], + fileMap: {}, }; diff --git a/yarn-project/end-to-end/tsconfig.web.json b/yarn-project/end-to-end/tsconfig.web.json index 94b4537fe51..890902dced6 100644 --- a/yarn-project/end-to-end/tsconfig.web.json +++ b/yarn-project/end-to-end/tsconfig.web.json @@ -8,5 +8,5 @@ { "path": "../aztec.js" } - ], + ] } diff --git a/yarn-project/foundation/src/abi/abi.ts b/yarn-project/foundation/src/abi/abi.ts index 9216c9805e9..406ff37aa99 100644 --- a/yarn-project/foundation/src/abi/abi.ts +++ b/yarn-project/foundation/src/abi/abi.ts @@ -1,5 +1,7 @@ import { inflate } from 'pako'; +import { type FunctionSelector } from './selector.js'; + /** * A named type. */ @@ -164,6 +166,10 @@ export interface FunctionArtifact extends FunctionAbi { * The verification key of the function. */ verificationKey?: string; + /** + * Maps opcodes to source code pointers + */ + debugSymbols: string; } /** @@ -227,20 +233,6 @@ export type DebugFileMap = Record< } >; -/** - * The debug metadata of an ABI. - */ -export interface DebugMetadata { - /** - * The DebugInfo object, deflated as JSON, compressed using gzip and serialized with base64. - */ - debugSymbols: string[]; - /** - * The map of file ID to the source code and path of the file. - */ - fileMap: DebugFileMap; -} - /** * Defines artifact of a contract. */ @@ -265,10 +257,9 @@ export interface ContractArtifact { events: EventAbi[]; /** - * The debug metadata of the contract. - * It's used to include the relevant source code section when a constraint is not met during simulation. + * The map of file ID to the source code and path of the file. */ - debug?: DebugMetadata; + fileMap: DebugFileMap; } /** @@ -285,6 +276,47 @@ export interface FunctionDebugMetadata { files: DebugFileMap; } +/** A function artifact with optional debug metadata */ +export interface FunctionArtifactWithDebugMetadata extends FunctionArtifact { + /** Debug metadata for the function. */ + debug?: FunctionDebugMetadata; +} + +/** + * Gets a function artifact given its name or selector. + */ +export function getFunctionArtifact( + artifact: ContractArtifact, + functionNameOrSelector: string | FunctionSelector, +): FunctionArtifact { + const functionArtifact = artifact.functions.find(f => + typeof functionNameOrSelector === 'string' + ? f.name === functionNameOrSelector + : functionNameOrSelector.equals(f.name, f.parameters), + ); + if (!functionArtifact) { + throw new Error(`Unknown function ${functionNameOrSelector}`); + } + return functionArtifact; +} + +/** @deprecated Use getFunctionArtifact instead */ +export function getFunctionArtifactWithSelector(artifact: ContractArtifact, selector: FunctionSelector) { + return getFunctionArtifact(artifact, selector); +} + +/** + * Gets a function artifact including debug metadata given its name or selector. + */ +export function getFunctionArtifactWithDebugMetadata( + artifact: ContractArtifact, + functionNameOrSelector: string | FunctionSelector, +): FunctionArtifactWithDebugMetadata { + const functionArtifact = getFunctionArtifact(artifact, functionNameOrSelector); + const debugMetadata = getFunctionDebugMetadata(artifact, functionArtifact); + return { ...functionArtifact, debug: debugMetadata }; +} + /** * Gets the debug metadata of a given function from the contract artifact * @param artifact - The contract build artifact @@ -292,19 +324,14 @@ export interface FunctionDebugMetadata { * @returns The debug metadata of the function */ export function getFunctionDebugMetadata( - artifact: ContractArtifact, - functionName: string, + contractArtifact: ContractArtifact, + functionArtifact: FunctionArtifact, ): FunctionDebugMetadata | undefined { - const functionIndex = artifact.functions.findIndex(f => f.name === functionName); - if (artifact.debug && functionIndex !== -1) { + if (functionArtifact.debugSymbols && contractArtifact.fileMap) { const debugSymbols = JSON.parse( - inflate(Buffer.from(artifact.debug.debugSymbols[functionIndex], 'base64'), { to: 'string' }), + inflate(Buffer.from(functionArtifact.debugSymbols, 'base64'), { to: 'string', raw: true }), ); - const files = artifact.debug.fileMap; - return { - debugSymbols, - files, - }; + return { debugSymbols, files: contractArtifact.fileMap }; } return undefined; } diff --git a/yarn-project/foundation/src/abi/selector.ts b/yarn-project/foundation/src/abi/selector.ts index 0c631418b29..ff70d868f03 100644 --- a/yarn-project/foundation/src/abi/selector.ts +++ b/yarn-project/foundation/src/abi/selector.ts @@ -3,7 +3,7 @@ import { BufferReader } from '@aztec/foundation/serialize'; import { keccak } from '../crypto/keccak/index.js'; import { Fr } from '../fields/index.js'; -import { ABIParameter } from './abi.js'; +import { type ABIParameter } from './abi.js'; import { decodeFunctionSignature } from './decoder.js'; /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ @@ -71,6 +71,19 @@ export interface FunctionSelector { /** A function selector is the first 4 bytes of the hash of a function signature. */ export class FunctionSelector extends Selector { + /** + * Checks if this function selector is equal to another. + * @returns True if the function selectors are equal. + */ + equals(otherName: string, otherParams: ABIParameter[]): boolean; + equals(other: FunctionSelector): boolean; + equals(other: FunctionSelector | string, otherParams?: ABIParameter[]): boolean { + if (typeof other === 'string') { + return this.equals(FunctionSelector.fromNameAndParameters(other, otherParams!)); + } + return this.value === other.value; + } + /** * Deserializes from a buffer or reader, corresponding to a write in cpp. * @param buffer - Buffer or BufferReader to read from. diff --git a/yarn-project/noir-compiler/package.json b/yarn-project/noir-compiler/package.json index af5b492ecfe..71c96a4db41 100644 --- a/yarn-project/noir-compiler/package.json +++ b/yarn-project/noir-compiler/package.json @@ -47,6 +47,7 @@ "dependencies": { "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/types": "workspace:^", "@iarna/toml": "^2.2.5", "@noir-lang/noir_wasm": "portal:../../noir/packages/noir_wasm", "base64-js": "^1.5.1", diff --git a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts index 4c1dd616c20..ab40a9b78e8 100644 --- a/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts +++ b/yarn-project/noir-compiler/src/cli/add_noir_compiler_commander_actions.ts @@ -18,15 +18,14 @@ export function addCodegenCommanderAction(program: Command, _: LogFn = () => {}) .command('codegen') .argument('', 'Path to the Noir ABI or project dir.') .option('-o, --outdir ', 'Output folder for the generated code.') - .option('-d, --debug', 'Include debug info.') .option('--ts', 'Generate TypeScript wrapper.') .option('--nr', 'Generate Noir interface.') .description('Validates and generates an Aztec Contract ABI from Noir ABI.') - .action(async (noirAbiPath: string, { debug, outdir, ts, nr }) => { + .action(async (noirAbiPath: string, { outdir, ts, nr }) => { if (ts && nr) { throw new Error('--ts and --nr are mutually exclusive.'); } const { generateCode } = await import('./codegen.js'); - generateCode(outdir || dirname(noirAbiPath), noirAbiPath, debug, ts, nr); + generateCode(outdir || dirname(noirAbiPath), noirAbiPath, { ts, nr }); }); } diff --git a/yarn-project/noir-compiler/src/cli/codegen.ts b/yarn-project/noir-compiler/src/cli/codegen.ts index 98eb5670d42..5d0234a7f84 100644 --- a/yarn-project/noir-compiler/src/cli/codegen.ts +++ b/yarn-project/noir-compiler/src/cli/codegen.ts @@ -1,35 +1,38 @@ -import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs'; +import { loadContractArtifact } from '@aztec/types/abi'; + +import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs'; import path from 'path'; -import { generateContractArtifact } from '../contract-interface-gen/abi.js'; -import { generateTypescriptContractInterface } from '../contract-interface-gen/contractTypescript.js'; import { generateNoirContractInterface } from '../contract-interface-gen/noir.js'; +import { generateTypescriptContractInterface } from '../contract-interface-gen/typescript.js'; + +/** 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, includeDebug = false, ts = false, nr = false) { +export function generateCode(outputPath: string, fileOrDirPath: string, opts: GenerateCodeOptions = {}) { const stats = statSync(fileOrDirPath); if (stats.isDirectory()) { const files = readdirSync(fileOrDirPath).filter(file => file.endsWith('.json') && !file.startsWith('debug_')); for (const file of files) { const fullPath = path.join(fileOrDirPath, file); - generateFromNoirAbi(outputPath, fullPath, includeDebug, ts, nr); + generateFromNoirAbi(outputPath, fullPath, opts); } } else if (stats.isFile()) { - generateFromNoirAbi(outputPath, fileOrDirPath, includeDebug, ts, nr); + generateFromNoirAbi(outputPath, fileOrDirPath, opts); } } /** - * + * Generates Noir interface or Typescript interface for a single file Noir compilation artifact. */ -function generateFromNoirAbi(outputPath: string, noirAbiPath: string, includeDebug: boolean, ts: boolean, nr: boolean) { +function generateFromNoirAbi(outputPath: string, noirAbiPath: string, opts: GenerateCodeOptions = {}) { const contract = JSON.parse(readFileSync(noirAbiPath, 'utf8')); - const noirDebugPath = includeDebug ? getDebugFilePath(noirAbiPath) : undefined; - const debug = noirDebugPath ? JSON.parse(readFileSync(noirDebugPath, 'utf8')) : undefined; - const aztecAbi = generateContractArtifact({ contract, debug }); + const aztecAbi = loadContractArtifact(contract); + const { nr, ts } = opts; mkdirSync(outputPath, { recursive: true }); @@ -39,20 +42,17 @@ function generateFromNoirAbi(outputPath: string, noirAbiPath: string, includeDeb return; } - writeFileSync(`${outputPath}/${aztecAbi.name}.json`, JSON.stringify(aztecAbi, undefined, 2)); - if (ts) { - const tsWrapper = generateTypescriptContractInterface(aztecAbi, `./${aztecAbi.name}.json`); + console.log('OUT', outputPath); + let relativeArtifactPath = path.relative(outputPath, noirAbiPath); + if (relativeArtifactPath === path.basename(noirAbiPath)) { + // Prepend ./ for local import if the folder is the same + relativeArtifactPath = `./${relativeArtifactPath}`; + } + + console.log(`PATHS`, path.dirname(outputPath), noirAbiPath, relativeArtifactPath); + + const tsWrapper = generateTypescriptContractInterface(aztecAbi, relativeArtifactPath); writeFileSync(`${outputPath}/${aztecAbi.name}.ts`, tsWrapper); } } - -/** - * - */ -function getDebugFilePath(filePath: string) { - const dirname = path.dirname(filePath); - const basename = path.basename(filePath); - const result = path.join(dirname, 'debug_' + basename); - return existsSync(result) ? result : undefined; -} diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/abi.ts b/yarn-project/noir-compiler/src/contract-interface-gen/abi.ts deleted file mode 100644 index 47473cdefa8..00000000000 --- a/yarn-project/noir-compiler/src/contract-interface-gen/abi.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { FUNCTION_TREE_HEIGHT } from '@aztec/circuits.js/constants'; -import { ContractArtifact, DebugMetadata, FunctionArtifact, FunctionType } from '@aztec/foundation/abi'; - -import { deflate } from 'pako'; - -import { mockVerificationKey } from '../mocked_keys.js'; -import { - NoirCompilationResult, - NoirContractCompilationArtifacts, - NoirFunctionEntry, - NoirProgramCompilationArtifacts, - ProgramArtifact, - isNoirContractCompilationArtifacts, - isNoirProgramCompilationArtifacts, -} from '../noir_artifact.js'; - -/** - * Generates a function build artifact. Replaces verification key with a mock value. - * @param fn - Noir function entry. - * @returns Function artifact. - */ -function generateFunctionArtifact(fn: NoirFunctionEntry): FunctionArtifact { - const functionType = fn.function_type.toLowerCase() as FunctionType; - const isInternal = fn.is_internal; - - // If the function is not unconstrained, the first item is inputs or CallContext which we should omit - let parameters = fn.abi.parameters; - if (functionType !== FunctionType.UNCONSTRAINED) { - parameters = parameters.slice(1); - } - - // If the function is secret, the return is the public inputs, which should be omitted - const returnTypes = functionType === FunctionType.SECRET ? [] : [fn.abi.return_type.abi_type]; - - return { - name: fn.name, - functionType, - isInternal, - parameters, - returnTypes, - bytecode: fn.bytecode, - verificationKey: mockVerificationKey, - }; -} - -/** - * Entrypoint for generating the .json artifact for compiled contract or program - * @param compileResult - Noir build output. - * @returns Aztec contract build artifact. - */ -export function generateArtifact(compileResult: NoirCompilationResult) { - if (isNoirContractCompilationArtifacts(compileResult)) { - return generateContractArtifact(compileResult); - } else if (isNoirProgramCompilationArtifacts(compileResult)) { - return generateProgramArtifact(compileResult); - } else { - throw Error('Unsupported artifact type'); - } -} - -/** - * Given a Nargo output generates an Aztec-compatible contract artifact. - * @param compiled - Noir build output. - * @returns Aztec contract build artifact. - */ -export function generateProgramArtifact( - { program }: NoirProgramCompilationArtifacts, - // eslint-disable-next-line camelcase - noir_version?: string, -): ProgramArtifact { - return { - // eslint-disable-next-line camelcase - noir_version, - hash: program.hash, - abi: program.abi, - - // TODO: should we parse and write the debug? it doesn't seem to be in the nargo output - // debug: someParsedDebug, - }; -} - -/** - * Given a Nargo output generates an Aztec-compatible contract artifact. - * @param compiled - Noir build output. - * @returns Aztec contract build artifact. - */ -export function generateContractArtifact( - { contract, debug }: NoirContractCompilationArtifacts, - aztecNrVersion?: string, -): ContractArtifact { - const constructorArtifact = contract.functions.find(({ name }) => name === 'constructor'); - if (constructorArtifact === undefined) { - throw new Error('Contract must have a constructor function'); - } - if (contract.functions.length > 2 ** FUNCTION_TREE_HEIGHT) { - throw new Error(`Contract can only have a maximum of ${2 ** FUNCTION_TREE_HEIGHT} functions`); - } - const originalFunctions = contract.functions; - // TODO why sort? we should have idempotent compilation so this should not be needed. - const sortedFunctions = [...contract.functions].sort((fnA, fnB) => fnA.name.localeCompare(fnB.name)); - let parsedDebug: DebugMetadata | undefined = undefined; - - if (debug) { - parsedDebug = { - debugSymbols: sortedFunctions.map(fn => { - const originalIndex = originalFunctions.indexOf(fn); - return Buffer.from(deflate(JSON.stringify(debug.debug_symbols[originalIndex]))).toString('base64'); - }), - fileMap: debug.file_map, - }; - } - - return { - name: contract.name, - functions: sortedFunctions.map(generateFunctionArtifact), - events: contract.events, - debug: parsedDebug, - aztecNrVersion, - }; -} diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/programTypescript.ts b/yarn-project/noir-compiler/src/contract-interface-gen/programTypescript.ts deleted file mode 100644 index 7961344b522..00000000000 --- a/yarn-project/noir-compiler/src/contract-interface-gen/programTypescript.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { ABIType } from '@aztec/foundation/abi'; -import { NoirFunctionAbi } from '@aztec/noir-compiler'; - -/** - * Keep track off all of the Noir primitive types that were used. - * Most of these will not have a 1-1 definition in TypeScript, - * so we will need to generate type aliases for them. - * - * We want to generate type aliases - * for specific types that are used in the ABI. - * - * For example: - * - If `Field` is used we want to alias that - * with `number`. - * - If `u32` is used we want to alias that with `number` too. - */ -type PrimitiveTypesUsed = { - /** - * The name of the type alias that we will generate. - */ - aliasName: string; - /** - * The TypeScript type that we will alias to. - */ - tsType: string; -}; - -const noirPrimitiveTypesToTsTypes = new Map(); - -/** - * Typescript does not allow us to check for equality of non-primitive types - * easily, so we create a addIfUnique function that will only add an item - * to the map if it is not already there by using JSON.stringify. - * @param item - The item to add to the map. - */ -function addIfUnique(item: PrimitiveTypesUsed) { - const key = JSON.stringify(item); - if (!noirPrimitiveTypesToTsTypes.has(key)) { - noirPrimitiveTypesToTsTypes.set(key, item); - } -} - -/** - * Converts an ABI type to a TypeScript type. - * @param type - The ABI type to convert. - * @returns The typescript code to define the type. - */ -function abiTypeToTs(type: ABIType): string { - switch (type.kind) { - case 'integer': { - let tsIntType = ''; - if (type.sign === 'signed') { - tsIntType = `i${type.width}`; - } else { - tsIntType = `u${type.width}`; - } - addIfUnique({ aliasName: tsIntType, tsType: 'string' }); - return tsIntType; - } - case 'boolean': - return `boolean`; - case 'array': - return `FixedLengthArray<${abiTypeToTs(type.type)}, ${type.length}>`; - case 'struct': - return getLastComponentOfPath(type.path); - case 'field': - addIfUnique({ aliasName: 'Field', tsType: 'string' }); - return 'Field'; - default: - throw new Error(`Unknown ABI type ${type}`); - } -} - -/** - * Returns the last component of a path, e.g. "foo::bar::baz" -\> "baz" - * Note: that if we have a path such as "Baz", we will return "Baz". - * - * Since these paths corresponds to structs, we can assume that we - * cannot have "foo::bar::". - * - * We also make the assumption that since these paths are coming from - * Noir, then we will not have two paths that look like this: - * - foo::bar::Baz - * - cat::dog::Baz - * ie the last component of the path (struct name) is enough to uniquely identify - * the whole path. - * - * TODO: We should double check this assumption when we use type aliases, - * I expect that `foo::bar::Baz as Dog` would effectively give `foo::bar::Dog` - * @param str - The path to get the last component of. - * @returns The last component of the path. - */ -function getLastComponentOfPath(str: string): string { - const parts = str.split('::'); - const lastPart = parts[parts.length - 1]; - return lastPart; -} - -/** - * Generates TypeScript interfaces for the structs used in the ABI. - * @param type - The ABI type to generate the interface for. - * @param output - The set of structs that we have already generated bindings for. - * @returns The TypeScript code to define the struct. - */ -function generateStructInterfaces(type: ABIType, output: Set): string { - let result = ''; - - // Edge case to handle the array of structs case. - if (type.kind === 'array' && type.type.kind === 'struct' && !output.has(getLastComponentOfPath(type.type.path))) { - result += generateStructInterfaces(type.type, output); - } - if (type.kind !== 'struct') { - return result; - } - - // List of structs encountered while viewing this type that we need to generate - // bindings for. - const typesEncountered = new Set(); - - // Codegen the struct and then its fields, so that the structs fields - // are defined before the struct itself. - let codeGeneratedStruct = ''; - let codeGeneratedStructFields = ''; - - const structName = getLastComponentOfPath(type.path); - if (!output.has(structName)) { - codeGeneratedStruct += `export interface ${structName} {\n`; - for (const field of type.fields) { - codeGeneratedStruct += ` ${field.name}: ${abiTypeToTs(field.type)};\n`; - typesEncountered.add(field.type); - } - codeGeneratedStruct += `}\n\n`; - output.add(structName); - - // Generate code for the encountered structs in the field above - for (const type of typesEncountered) { - codeGeneratedStructFields += generateStructInterfaces(type, output); - } - } - - return codeGeneratedStructFields + '\n' + codeGeneratedStruct; -} - -/** - * Generates a TypeScript interface for the ABI. - * @param abiObj - The ABI to generate the interface for. - * @returns The TypeScript code to define the interface. - */ -export function generateTypescriptProgramInterface(abiObj: NoirFunctionAbi): string { - let result = ``; - const outputStructs = new Set(); - - // Define structs for composite types - for (const param of abiObj.parameters) { - result += generateStructInterfaces(param.type, outputStructs); - } - - // Generating Return type, if it exists - // - if (abiObj.return_type != null) { - result += generateStructInterfaces(abiObj.return_type.abi_type, outputStructs); - result += `export type ReturnType = ${abiTypeToTs(abiObj.return_type.abi_type)};\n`; - } - - // Generating Input type - result += '\nexport interface InputType {\n'; - for (const param of abiObj.parameters) { - result += ` ${param.name}: ${abiTypeToTs(param.type)};\n`; - } - result += '}'; - - // Add the primitive Noir types that do not have a 1-1 mapping to TypeScript. - let primitiveTypeAliases = ''; - for (const [, value] of noirPrimitiveTypesToTsTypes) { - primitiveTypeAliases += `\nexport type ${value.aliasName} = ${value.tsType};`; - } - - const fixedLengthArray = - '\nexport type FixedLengthArray = L extends 0 ? never[]: T[] & { length: L }'; - - return ( - `/* Autogenerated file, do not edit! */\n\n/* eslint-disable */\n` + - fixedLengthArray + - '\n' + - primitiveTypeAliases + - '\n' + - result - ); -} diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/contractTypescript.ts b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts similarity index 97% rename from yarn-project/noir-compiler/src/contract-interface-gen/contractTypescript.ts rename to yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts index 7f37f9e4923..c17d0d7abb4 100644 --- a/yarn-project/noir-compiler/src/contract-interface-gen/contractTypescript.ts +++ b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts @@ -154,7 +154,7 @@ function generateArtifactGetter(name: string) { function generateAbiStatement(name: string, artifactImportPath: string) { const stmts = [ `import ${name}ContractArtifactJson from '${artifactImportPath}' assert { type: 'json' };`, - `export const ${name}ContractArtifact = ${name}ContractArtifactJson as ContractArtifact;`, + `export const ${name}ContractArtifact = loadContractArtifact(${name}ContractArtifactJson as NoirCompiledContract);`, ]; return stmts.join('\n'); } @@ -192,6 +192,8 @@ import { FieldLike, Fr, FunctionSelectorLike, + loadContractArtifact, + NoirCompiledContract, Point, PublicKey, Wallet, diff --git a/yarn-project/noir-compiler/src/index.ts b/yarn-project/noir-compiler/src/index.ts index 836c2e8b2c9..88416ef4282 100644 --- a/yarn-project/noir-compiler/src/index.ts +++ b/yarn-project/noir-compiler/src/index.ts @@ -1,8 +1,4 @@ export * from './versions.js'; -export { generateTypescriptContractInterface } from './contract-interface-gen/contractTypescript.js'; +export { generateTypescriptContractInterface } from './contract-interface-gen/typescript.js'; export { generateNoirContractInterface } from './contract-interface-gen/noir.js'; -export { generateTypescriptProgramInterface } from './contract-interface-gen/programTypescript.js'; -export { generateContractArtifact } from './contract-interface-gen/abi.js'; - -export * from './noir_artifact.js'; diff --git a/yarn-project/noir-compiler/tsconfig.json b/yarn-project/noir-compiler/tsconfig.json index a6b3ad94790..00bc12a18a0 100644 --- a/yarn-project/noir-compiler/tsconfig.json +++ b/yarn-project/noir-compiler/tsconfig.json @@ -11,6 +11,9 @@ }, { "path": "../foundation" + }, + { + "path": "../types" } ], "include": ["src", "src/*.json"] diff --git a/yarn-project/noir-contracts/bootstrap.sh b/yarn-project/noir-contracts/bootstrap.sh deleted file mode 100755 index 43284ba2a79..00000000000 --- a/yarn-project/noir-contracts/bootstrap.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -yarn noir:build:all diff --git a/yarn-project/noir-contracts/package.json b/yarn-project/noir-contracts/package.json index cd81311cf34..da109ff5b09 100644 --- a/yarn-project/noir-contracts/package.json +++ b/yarn-project/noir-contracts/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "type": "module", "exports": { - ".": "./dest/index.js", - "./*": "./dest/*.js" + ".": "./dest/src/index.js", + "./*": "./dest/src/*.js" }, "scripts": { "build": "yarn clean && yarn build:contracts && tsc -b", diff --git a/yarn-project/noir-contracts/scripts/compile.sh b/yarn-project/noir-contracts/scripts/compile.sh index 214e441409b..671599aa874 100755 --- a/yarn-project/noir-contracts/scripts/compile.sh +++ b/yarn-project/noir-contracts/scripts/compile.sh @@ -3,4 +3,4 @@ set -euo pipefail echo "Compiling contracts..." -../../noir/target/release/nargo compile --silence-warnings +nargo compile --silence-warnings diff --git a/yarn-project/noir-contracts/scripts/generate-types.sh b/yarn-project/noir-contracts/scripts/generate-types.sh index 6901ab97f40..8d11bb1943d 100755 --- a/yarn-project/noir-contracts/scripts/generate-types.sh +++ b/yarn-project/noir-contracts/scripts/generate-types.sh @@ -7,18 +7,17 @@ INDEX="$OUT_DIR/index.ts" rm -rf $OUT_DIR && mkdir -p $OUT_DIR # Generate index.ts header. -echo "// Auto generated module - do not edit!\n" > $INDEX +echo "// Auto generated module - do not edit!" > $INDEX for ABI in $(find target -maxdepth 1 -type f ! -name 'debug_*' -name '*.json'); do CONTRACT=$(jq -r .name $ABI) - DEBUG_INFO="$(dirname $ABI)/debug_$(basename $ABI)" echo "Creating types for $CONTRACT in $ABI..." - node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR -d --ts $ABI + node --no-warnings ../noir-compiler/dest/cli.js codegen -o $OUT_DIR --ts $ABI # Add contract import/export to index.ts. echo "export * from './${CONTRACT}.js';" >> $INDEX done echo "Formatting..." -yarn formatting:fix + diff --git a/yarn-project/noir-contracts/scripts/transform_json_abi.sh b/yarn-project/noir-contracts/scripts/transform_json_abi.sh deleted file mode 100755 index bc27163ee6f..00000000000 --- a/yarn-project/noir-contracts/scripts/transform_json_abi.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# Sadly, yarn-project expects ABI files in a format different to what is output from nargo. -# This provides a tool to quickly do the transform with jq. -# It's not currently used as the transform is done in TypeScript when we generate the contract wrapper. -# TODO: Why don't our contract classes just work with noir abis? -# -# Lowercase function_type value. -# Camel case function_type and is_internal. -# Discard first parameter (input) if function is not unconstrained. -# Hoist parameters out of abi. -# Hoist return_type out of abi, make an array of 1 element, or empty array if function is secret. -# -jq ' - .functions |= map( - (.functionType = (.function_type | ascii_downcase)) | - (.isInternal = .is_internal) | - del(.function_type, .is_internal) | - (.parameters = if .functionType == "unconstrained" then .abi.parameters else .abi.parameters[1:] end) | - (.returnTypes = if .functionType == "secret" then [] else [ .abi.return_type.abi_type ] end) | - del(.abi) - ) -' $1 diff --git a/yarn-project/noir-contracts/tsconfig.json b/yarn-project/noir-contracts/tsconfig.json index a82cec59baa..bf3da7b0006 100644 --- a/yarn-project/noir-contracts/tsconfig.json +++ b/yarn-project/noir-contracts/tsconfig.json @@ -2,7 +2,7 @@ "extends": "..", "compilerOptions": { "outDir": "dest", - "rootDir": "src", + "rootDir": ".", "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ @@ -12,9 +12,7 @@ ], "include": [ "src", - "src/**/*.json" + "target", + "target/*.json", ], - "exclude": [ - "src/contracts" - ] } diff --git a/yarn-project/noir-protocol-circuits/package.json b/yarn-project/noir-protocol-circuits/package.json index 1a1bd96cb30..358e89ccc14 100644 --- a/yarn-project/noir-protocol-circuits/package.json +++ b/yarn-project/noir-protocol-circuits/package.json @@ -7,12 +7,12 @@ "./types": "./dest/types/index.js" }, "scripts": { - "build": "yarn clean && yarn noir:build && yarn noir:types", + "build": "yarn clean && yarn noir:build && yarn noir:types && tsc -b", "clean": "rm -rf ./dest .tsbuildinfo src/types src/target", "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "NODE_OPTIONS='--max-old-space-size=8096' run -T eslint --fix ./src && run -T prettier -w ./src", "formatting:fix:types": "NODE_OPTIONS='--max-old-space-size=8096' run -T eslint --fix ./src/types && run -T prettier -w ./src/types", - "noir:build": "cd src && ../../../noir/target/release/nargo compile --silence-warnings && rm -rf ./target/debug_*", + "noir:build": "cd src && ../../../noir/target/release/nargo compile --silence-warnings", "noir:types": "node --loader ts-node/esm src/scripts/generate_ts_from_abi.ts && yarn formatting:fix:types", "noir:test": "cd src && ../../../noir/target/release/nargo test", "test": "yarn test:js && yarn noir:test", @@ -30,6 +30,7 @@ "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/noir-compiler": "workspace:^", + "@aztec/types": "workspace:^", "@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js", "@noir-lang/backend_barretenberg": "portal:../../noir/packages/backend_barretenberg", "@noir-lang/noir_js": "portal:../../noir/packages/noir_js", diff --git a/yarn-project/noir-protocol-circuits/src/index.ts b/yarn-project/noir-protocol-circuits/src/index.ts index 89954211e99..05b346011be 100644 --- a/yarn-project/noir-protocol-circuits/src/index.ts +++ b/yarn-project/noir-protocol-circuits/src/index.ts @@ -11,7 +11,7 @@ import { RootRollupInputs, RootRollupPublicInputs, } from '@aztec/circuits.js'; -import { NoirCompiledCircuit } from '@aztec/noir-compiler'; +import { NoirCompiledCircuit } from '@aztec/types/noir'; import { WasmBlackBoxFunctionSolver, createBlackBoxSolver, executeCircuitWithBlackBoxSolver } from '@noir-lang/acvm_js'; import { Abi, abiDecode, abiEncode } from '@noir-lang/noirc_abi'; diff --git a/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts b/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts index b3c5d16482a..6fc683cc162 100644 --- a/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts +++ b/yarn-project/noir-protocol-circuits/src/scripts/generate_ts_from_abi.ts @@ -1,6 +1,6 @@ import { ABIType } from '@aztec/foundation/abi'; import { createConsoleLogger } from '@aztec/foundation/log'; -import { NoirCompiledCircuit, NoirFunctionAbi } from '@aztec/noir-compiler'; +import { NoirCompiledCircuit, NoirFunctionAbi } from '@aztec/types/noir'; import fs from 'fs/promises'; diff --git a/yarn-project/noir-protocol-circuits/tsconfig.json b/yarn-project/noir-protocol-circuits/tsconfig.json index 4c2a3c5e245..917d509e475 100644 --- a/yarn-project/noir-protocol-circuits/tsconfig.json +++ b/yarn-project/noir-protocol-circuits/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../noir-compiler" }, + { + "path": "../types" + }, { "path": "../circuit-types" }, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 7af6ca539d7..d33010931b1 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -1,4 +1,4 @@ -import { DBOracle, FunctionArtifactWithDebugMetadata, MessageLoadOracleInputs } from '@aztec/acir-simulator'; +import { DBOracle, MessageLoadOracleInputs } from '@aztec/acir-simulator'; import { KeyStore, L2Block, @@ -17,6 +17,7 @@ import { GrumpkinPrivateKey, PublicKey, } from '@aztec/circuits.js'; +import { FunctionArtifactWithDebugMetadata } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; diff --git a/yarn-project/types/.eslintrc.cjs b/yarn-project/types/.eslintrc.cjs index 30064fcb134..e659927475c 100644 --- a/yarn-project/types/.eslintrc.cjs +++ b/yarn-project/types/.eslintrc.cjs @@ -1 +1 @@ -module.exports = require('@aztec/foundation/eslint'); \ No newline at end of file +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index 2ebd3ab048d..f8e42023289 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -6,8 +6,10 @@ "main": "./dest/index.js", "types": "./dest/index.d.ts", "exports": { + "./abi": "./dest/abi/index.js", "./interfaces": "./dest/interfaces/index.js", - "./membership": "./dest/sibling-path/index.js" + "./membership": "./dest/sibling-path/index.js", + "./noir": "./dest/noir/index.js" }, "scripts": { "build": "yarn clean && tsc -b", diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts new file mode 100644 index 00000000000..1df2e3dbac1 --- /dev/null +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -0,0 +1,130 @@ +import { + ABIParameter, + ABIParameterVisibility, + ABIType, + ContractArtifact, + FunctionArtifact, + FunctionType, +} from '@aztec/foundation/abi'; + +import { NoirCompiledContract } from '../noir/index.js'; +import { mockVerificationKey } from './mocked_keys.js'; + +/** + * Gets nargo build output and returns a valid contract artifact instance. + * @param input - Input object as generated by nargo compile. + * @returns A valid contract artifact instance. + */ +export function loadContractArtifact(input: NoirCompiledContract): ContractArtifact { + if (isContractArtifact(input)) { + return input; + } + const contractArtifact = generateContractArtifact(input); + validateContractArtifact(contractArtifact); + return contractArtifact; +} + +/** + * Checks if the given input looks like a valid ContractArtifact. The check is not exhaustive, + * and it's just meant to differentiate between nargo raw build artifacts and the ones + * produced by this compiler. + * @param input - Input object. + * @returns True if it looks like a ContractArtifact. + */ +function isContractArtifact(input: any): input is ContractArtifact { + if (typeof input !== 'object') { + return false; + } + const maybeContractArtifact = input as ContractArtifact; + if (typeof maybeContractArtifact.name !== 'string') { + return false; + } + if (!Array.isArray(maybeContractArtifact.functions)) { + return false; + } + for (const fn of maybeContractArtifact.functions) { + if (typeof fn.name !== 'string') { + return false; + } + if (typeof fn.functionType !== 'string') { + return false; + } + } + return true; +} + +/** Parameter in a function from a noir contract compilation artifact */ +type NoirCompiledContractFunctionParameter = NoirCompiledContractFunction['abi']['parameters'][number]; + +/** + * Generates a function parameter out of one generated by a nargo build. + * @param param - Noir parameter. + * @returns A function parameter. + */ +function generateFunctionParameter(param: NoirCompiledContractFunctionParameter): ABIParameter { + const { visibility } = param; + if ((visibility as string) === 'databus') { + throw new Error(`Unsupported visibility ${param.visibility} for noir contract function parameter ${param.name}.`); + } + return { ...param, visibility: visibility as ABIParameterVisibility }; +} + +/** Function from a noir contract compilation artifact */ +type NoirCompiledContractFunction = NoirCompiledContract['functions'][number]; + +/** + * Generates a function build artifact. Replaces verification key with a mock value. + * @param fn - Noir function entry. + * @returns Function artifact. + */ +function generateFunctionArtifact(fn: NoirCompiledContractFunction): FunctionArtifact { + const functionType = fn.function_type.toLowerCase() as FunctionType; + const isInternal = fn.is_internal; + + // If the function is not unconstrained, the first item is inputs or CallContext which we should omit + let parameters = fn.abi.parameters.map(generateFunctionParameter); + if (functionType !== 'unconstrained') { + parameters = parameters.slice(1); + } + + // If the function is secret, the return is the public inputs, which should be omitted + let returnTypes: ABIType[] = []; + if (functionType !== 'secret' && fn.abi.return_type) { + returnTypes = [fn.abi.return_type.abi_type]; + } + + return { + name: fn.name, + functionType, + isInternal, + parameters, + returnTypes, + bytecode: fn.bytecode, + verificationKey: mockVerificationKey, + debugSymbols: fn.debug_symbols, + }; +} + +/** Validates contract artifact instance, throwing on error. */ +function validateContractArtifact(contract: ContractArtifact) { + const constructorArtifact = contract.functions.find(({ name }) => name === 'constructor'); + if (constructorArtifact === undefined) { + throw new Error('Contract must have a constructor function'); + } + return contract; +} + +/** + * Given a Nargo output generates an Aztec-compatible contract artifact. + * @param compiled - Noir build output. + * @returns Aztec contract build artifact. + */ +function generateContractArtifact(contract: NoirCompiledContract, aztecNrVersion?: string): ContractArtifact { + return { + name: contract.name, + functions: contract.functions.map(generateFunctionArtifact), + events: contract.events, + fileMap: contract.file_map, + aztecNrVersion, + }; +} diff --git a/yarn-project/types/src/abi/index.ts b/yarn-project/types/src/abi/index.ts new file mode 100644 index 00000000000..27174adec05 --- /dev/null +++ b/yarn-project/types/src/abi/index.ts @@ -0,0 +1 @@ +export * from './contract_artifact.js'; diff --git a/yarn-project/types/src/abi/mocked_keys.ts b/yarn-project/types/src/abi/mocked_keys.ts new file mode 100644 index 00000000000..b8f5c677ead --- /dev/null +++ b/yarn-project/types/src/abi/mocked_keys.ts @@ -0,0 +1,2 @@ +export const mockVerificationKey = + '0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f'; diff --git a/yarn-project/noir-compiler/src/noir_artifact.ts b/yarn-project/types/src/noir/index.ts similarity index 77% rename from yarn-project/noir-compiler/src/noir_artifact.ts rename to yarn-project/types/src/noir/index.ts index f8702b3ca0f..65d7c88cb29 100644 --- a/yarn-project/noir-compiler/src/noir_artifact.ts +++ b/yarn-project/types/src/noir/index.ts @@ -10,20 +10,15 @@ import { /** The Aztec.nr function types. */ type NoirFunctionType = 'Open' | 'Secret' | 'Unconstrained'; +/** The witness indices of the parameters. */ +type ParamWitnessIndices = { /** Start */ start: number; /** End */ end: number }; + /** The ABI of an Aztec.nr function. */ export interface NoirFunctionAbi { /** The parameters of the function. */ parameters: ABIParameter[]; /** The witness indices of the parameters. Indexed by parameter name. */ - param_witnesses: { - /** input */ - input: { - /** start */ - start: number; - /** end */ - end: number; - }[]; - }; + param_witnesses: { [key: string]: undefined | ParamWitnessIndices[] }; /** The return type of the function. */ return_type: { /** @@ -54,9 +49,11 @@ export interface NoirFunctionEntry { /** The bytecode of the function in base64. */ bytecode: string; /** The proving key. */ - proving_key: string; + proving_key?: string; /** The verification key. */ - verification_key: string; + verification_key?: string; + /** The debug information, compressed and base64 encoded. */ + debug_symbols: string; } /** @@ -69,6 +66,8 @@ export interface NoirCompiledContract { functions: NoirFunctionEntry[]; /** The events of the contract */ events: EventAbi[]; + /** The map of file ID to the source code and path of the file. */ + file_map: DebugFileMap; } /** @@ -83,35 +82,10 @@ export interface NoirCompiledCircuit { abi: NoirFunctionAbi; /** The bytecode of the circuit in base64. */ bytecode: string; -} - -/** - * Defines artifact of a contract. - */ -export interface ProgramArtifact { - /** - * version of noir used to compile - */ - noir_version?: string; - /** - * the name of the project, read from Nargo.toml - */ - name?: string; - /** - * The hash of the contract. - */ - hash?: number; - - /** - * The abi of the program. - */ - abi: any; // TODO: type - - /** - * The debug metadata of the contract. - * It's used to include the relevant source code section when a constraint is not met during simulation. - */ - debug?: NoirDebugMetadata; + /** The debug information, compressed and base64 encoded. */ + debug_symbols: string; + /** The map of file ID to the source code and path of the file. */ + file_map: DebugFileMap; } /** @@ -136,11 +110,6 @@ export interface NoirContractCompilationArtifacts { * The compiled contract. */ contract: NoirCompiledContract; - - /** - * The artifact that contains the debug metadata about the contract. - */ - debug?: NoirDebugMetadata; } /** @@ -155,11 +124,6 @@ export interface NoirProgramCompilationArtifacts { * The compiled contract. */ program: NoirCompiledCircuit; - - /** - * The artifact that contains the debug metadata about the contract. - */ - debug?: NoirDebugMetadata; } /** diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index c5e752f4d2f..ccd7eed53ed 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -608,6 +608,7 @@ __metadata: dependencies: "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/types": "workspace:^" "@iarna/toml": ^2.2.5 "@jest/globals": ^29.5.0 "@noir-lang/noir_wasm": "portal:../../noir/packages/noir_wasm" @@ -663,6 +664,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/merkle-tree": "workspace:^" "@aztec/noir-compiler": "workspace:^" + "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js" "@noir-lang/backend_barretenberg": "portal:../../noir/packages/backend_barretenberg"